/* * 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.sql.DatabaseMetaData; import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import ca.sqlpower.dao.SPPersister; import ca.sqlpower.object.ObjectDependentException; import ca.sqlpower.object.SPObject; import ca.sqlpower.object.annotation.Accessor; import ca.sqlpower.object.annotation.Constructor; import ca.sqlpower.object.annotation.ConstructorParameter; import ca.sqlpower.object.annotation.ConstructorParameter.ParameterType; import ca.sqlpower.object.annotation.Mutator; import ca.sqlpower.object.annotation.NonProperty; import ca.sqlpower.object.annotation.Transient; import ca.sqlpower.sql.JDBCDataSourceType; import ca.sqlpower.sqlobject.SQLTypePhysicalProperties.SQLTypeConstraint; import ca.sqlpower.util.SQLPowerUtils; /** * An implementation of {@link SQLTypePhysicalPropertiesProvider} * that provides support for multiple SQLTypePhysicalProperties, * keyed by each physical platform's logical name (Typically this would be * retrieved from the platform's corresponding * {@link JDBCDataSourceType#getName()}. It will also be annotated to allow * generation of a corresponding {@link SPPersister}. */ public class UserDefinedSQLType extends SQLObject implements SQLTypePhysicalPropertiesProvider { /** * An unmodifiable {@link List} of allowed child types */ public static final List<Class<? extends SPObject>> allowedChildTypes = Collections.<Class<? extends SPObject>>singletonList(SQLTypePhysicalProperties.class); /** * The {@link UserDefinedSQLType} may have an 'upstream' type from which it * inherits properties from. */ private UserDefinedSQLType upstreamType; /** * A generic, platform-independent, high-level data type that describes this * particular SQLType. */ private BasicSQLType basicType; /** * The Default {@link SQLTypePhysicalProperties} for this type. By default, * requests for physical properties for all platforms from this type will * use this instance, UNLESS an overriding instance exists for the requested * platform in the {@link #overridingPhysicalProperties} list. All requests * for platform of * {@link SQLTypePhysicalPropertiesProvider#GENERIC_PLATFORM} will return * this default instance. */ private final SQLTypePhysicalProperties defaultPhysicalProperties; /** * A list of {@link SQLTypePhyisicalProperties} that are the children of * this object. These {@link SQLTypePhysicalProperties} instances are * overrides for the {@link #defaultPhysicalProperties} specific to a given * platform. Only one physical properties object should be listed per * platform. */ private List<SQLTypePhysicalProperties> overridingPhysicalProperties = new ArrayList<SQLTypePhysicalProperties>(); /** * A textual description of this type for documentation purposes. */ private String description; /** * The int value that corresponds to a type defined in {@link Types} that * this {@link UserDefinedSQLType} represents. */ private Integer type; /** * Specifies whether this type accepts NULL as a value, based on the values * specified by {@link DatabaseMetaData}. These include: * <ul> * <li>{@link DatabaseMetaData#columnNoNulls}</li> * <li>{@link DatabaseMetaData#columnNullable}</li> * <li>{@link DatabaseMetaData#columnNullableUnknown}</li> * </ul> */ private Integer myNullability; /** * This property indicates that values stored in this column should * default to some automatically-incrementing sequence of values. Every * database platform handles the specifics of this a little differently, * but the DDL generators are responsible for taking care of that. */ private Boolean myAutoIncrement; /** * Constructs a {@link UserDefinedSQLType} with a default * {@link SQLTypePhysicalProperties} set with a platform of * {@link SQLTypePhysicalPropertiesProvider#GENERIC_PLATFORM} */ //TODO: remove this constructor since this uses the old heathen ways public UserDefinedSQLType() { this("UserDefinedSQLType", null, null, null, null, new SQLTypePhysicalProperties(GENERIC_PLATFORM)); } /** * Constructs a {@link UserDefinedSQLType} with the * {@link #defaultPhysicalProperties} set to the * {@link SQLTypePhysicalProperties} in the argument. * * @param defaultPhysicalProperties * Sets the defaultPhysicalProperties to this instance */ //XXX While not mandatory to match the default physical properties child object //could probably use a better name than "primaryKeyIndex" to be stored under in //the JCR as it is misleading. @Constructor public UserDefinedSQLType( @ConstructorParameter(propertyName = "name") String name, @ConstructorParameter(propertyName = "myNullability") Integer nullability, @ConstructorParameter(propertyName = "myAutoIncrement") Boolean autoIncrement, @ConstructorParameter(propertyName = "basicType") BasicSQLType basicType, @ConstructorParameter(propertyName = "upstreamType") UserDefinedSQLType upstreamType, @ConstructorParameter(parameterType = ParameterType.CHILD, propertyName = "primaryKeyIndex") SQLTypePhysicalProperties defaultPhysicalProperties) { super(); this.defaultPhysicalProperties = defaultPhysicalProperties; setName(name); defaultPhysicalProperties.setParent(this); setPopulated(true); setMyNullability(nullability); setMyAutoIncrement(autoIncrement); setBasicType(basicType); setUpstreamType(upstreamType); } /** * Returns an overriding {@link SQLTypePhysicalProperties} for the given * platform name if one exists. Otherwise, it returns the * {@link #defaultPhysicalProperties}. * * @param platformName * The platform name to return a * {@link SQLTypePhysicalProperties} for. * @return If an overriding instance exists in this type, then return it. * Otherwise, it returns the {@link #defaultPhysicalProperties}. */ @NonProperty public SQLTypePhysicalProperties getPhysicalProperties(String platformName) { if (!GENERIC_PLATFORM.equals(platformName)) { for (SQLTypePhysicalProperties properties : overridingPhysicalProperties) { if (properties.getPlatform().equals(platformName)) return properties; } } return defaultPhysicalProperties; } @Override public List<? extends SQLObject> getChildrenWithoutPopulating() { ArrayList<SQLTypePhysicalProperties> properties = new ArrayList<SQLTypePhysicalProperties>(); properties.add(defaultPhysicalProperties); properties.addAll(overridingPhysicalProperties); return Collections.unmodifiableList(properties); } @Override public String getShortDisplayName() { return getName(); } @Override protected void populateImpl() throws SQLObjectException {} @Override protected boolean removeChildImpl(SPObject child) { int childIndex = overridingPhysicalProperties.indexOf(child); boolean removedFromList = overridingPhysicalProperties.remove(child); if (child != null && removedFromList) { //The defaultPhysicalProperties is the first physical properties in the first position. fireChildRemoved(SQLTypePhysicalProperties.class, child, childIndex + 1); child.setParent(null); return true; } else { return false; } } public List<Class<? extends SPObject>> getAllowedChildTypes() { return allowedChildTypes; } public List<? extends SPObject> getDependencies() { return Collections.singletonList(getUpstreamType()); } public void removeDependency(SPObject dependency) { if (dependency == getUpstreamType()) { setUpstreamType(null); } } @Mutator public void setBasicType(BasicSQLType basicType) { begin("Setting basic type."); BasicSQLType oldValue = this.basicType; this.basicType = basicType; firePropertyChange("basicType", oldValue, basicType); // Hack to set the appropriate "type" property given a basic type // At current, we are using the most generic SQL type so that all // the properties within the data type, such as precision and scale, // can be used. if (getType() == null) { setType(BasicSQLType.convertFromBasicSQLType(basicType)); } commit(); } @Accessor public BasicSQLType getBasicType() { if (basicType == null && upstreamType != null) { return upstreamType.getBasicType(); } else { return basicType; } } /** * Gets the check constraints for this type. The check constraint is only * valid if {@link #getConstraintType(String)} returns * {@link SQLTypeConstraint#CHECK} * * @return The {@link List} of {@link SQLCheckConstraint}s. */ @NonProperty public List<SQLCheckConstraint> getCheckConstraints(String platform) { List<SQLCheckConstraint> checkConstraints = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { checkConstraints = properties.getCheckConstraints(); if (checkConstraints.isEmpty() && getUpstreamType() != null) { checkConstraints = getUpstreamType().getCheckConstraints(platform); } } else if (getUpstreamType() != null) { checkConstraints = getUpstreamType().getCheckConstraints(platform); } else { checkConstraints = Collections.emptyList(); } return Collections.unmodifiableList(checkConstraints); } @NonProperty public SQLTypeConstraint getConstraintType(String platform) { SQLTypeConstraint constraintType = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { constraintType = properties.getConstraintType(); if (constraintType == null && getUpstreamType() != null) { constraintType = getUpstreamType().getConstraintType(platform); } } else if (getUpstreamType() != null) { constraintType = getUpstreamType().getConstraintType(platform); } return constraintType; } @NonProperty public String getDefaultValue(String platform) { String defaultValue = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { defaultValue = properties.getDefaultValue(); if (defaultValue == null && getUpstreamType() != null) { defaultValue = getUpstreamType().getDefaultValue(platform); } } else if (getUpstreamType() != null) { defaultValue = getUpstreamType().getDefaultValue(platform); } return defaultValue; } @NonProperty public List<SQLEnumeration> getEnumerations(String platform) { List<SQLEnumeration> enumerations = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { enumerations = properties.getChildren(SQLEnumeration.class); if (enumerations.isEmpty() && getUpstreamType() != null) { enumerations = getUpstreamType().getEnumerations(platform); } } else if (getUpstreamType() != null) { enumerations = getUpstreamType().getEnumerations(platform); } else { enumerations = Collections.emptyList(); } return Collections.unmodifiableList(enumerations); } @NonProperty public int getPrecision(String platform) { Integer precision = null; // A non-applicable precision should just mean that precision is 0. if (getPrecisionType(platform) != PropertyType.NOT_APPLICABLE) { SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { precision = properties.getPrecision(); // Get the precision property from the upstream type if this one // does not exist or its precision type is constant. if (getUpstreamType() != null && (precision == null || getUpstreamType() .getPrecisionType(platform) == PropertyType.CONSTANT)) { precision = getUpstreamType().getPrecision(platform); } } else if (getUpstreamType() != null) { precision = getUpstreamType().getPrecision(platform); } } // If precision is null and all upstream types also return null, then just return 0 to prevent an NPE return precision == null ? 0 : precision; } @NonProperty public int getScale(String platform) { Integer scale = null; // A non-applicable scale should just mean that scale is 0. if (getScaleType(platform) != PropertyType.NOT_APPLICABLE) { SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { scale = properties.getScale(); // Get the scale property from the upstream type if this one // does not exist or its scale type is constant. if (getUpstreamType() != null && (scale == null || getUpstreamType().getScaleType(platform) == PropertyType.CONSTANT)) { scale = getUpstreamType().getScale(platform); } } else if (getUpstreamType() != null) { scale = getUpstreamType().getScale(platform); } } // If scale is null and all upstream types also return null, then just return 0 to prevent an NPE return scale == null ? 0 : scale; } @Accessor public Integer getType() { if (type == null && upstreamType != null) { return upstreamType.getType(); } else { return type; } } public void addCheckConstraint(String platform, SQLCheckConstraint checkConstraint) { getOrCreatePhysicalProperties(platform).addCheckConstraint(checkConstraint); } /** * Adds a {@link SQLCheckConstraint} that is enforced on any * {@link SQLObject} that uses this type. * * @param platform * The platform to enforce the constraint on. * @param checkConstraint * The {@link SQLCheckConstraint} to enforce. */ public void addCheckConstraint(String platform, SQLCheckConstraint checkConstraint, int index) { getOrCreatePhysicalProperties(platform).addCheckConstraint(checkConstraint, index); } /** * Removes a {@link SQLCheckConstraint} from the child {@link List} of check * constraints that is being enforced on a {@link SQLObject} that uses this * type. * * @param platform * The platform to remove the enforced constraint from. * @param checkConstraint * The {@link SQLCheckConstraint} to remove. */ public boolean removeCheckConstraint(String platform, SQLCheckConstraint checkConstraint) { return getOrCreatePhysicalProperties(platform).removeCheckConstraint(checkConstraint); } @NonProperty public void setConstraintType(String platform, SQLTypeConstraint constraint) { getOrCreatePhysicalProperties(platform).setConstraintType(constraint); } @NonProperty public void setDefaultValue(String platform, String defaultValue) { getOrCreatePhysicalProperties(platform).setDefaultValue(defaultValue); } @NonProperty public void addEnumeration(String platform, SQLEnumeration enumeration) { getOrCreatePhysicalProperties(platform).addEnumeration(enumeration); } public void addEnumeration(String platform, SQLEnumeration enumeration, int index) { getOrCreatePhysicalProperties(platform).addEnumeration(enumeration, index); } /** * Sets the name of the {@link SQLTypePhysicalProperties} object keyed with * the given platform * * @param platform * The platform key that identifies the * {@link SQLTypePhysicalProperties} to name. * @param name * The new name value to set to */ @NonProperty public void setPhysicalTypeName(String platform, String name) { getOrCreatePhysicalProperties(platform).setName(name); } @NonProperty public void setPrecision(String platform, Integer precision) { getOrCreatePhysicalProperties(platform).setPrecision(precision); } @NonProperty public void setScale(String platform, Integer scale) { getOrCreatePhysicalProperties(platform).setScale(scale); } @Mutator public void setType(Integer type) { begin("Setting type."); Integer oldValue = getType(); this.type = type; firePropertyChange("type", oldValue, type); commit(); } @NonProperty public PropertyType getPrecisionType(String platform) { PropertyType precisionType = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { precisionType = properties.getPrecisionType(); if (precisionType == null && getUpstreamType() != null) { precisionType = getUpstreamType().getPrecisionType(platform); } } else if (getUpstreamType() != null) { precisionType = getUpstreamType().getPrecisionType(platform); } if (precisionType == null) { precisionType = PropertyType.NOT_APPLICABLE; } return precisionType; } @NonProperty public PropertyType getScaleType(String platform) { PropertyType scaleType = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { scaleType = properties.getScaleType(); if (scaleType == null && getUpstreamType() != null) { scaleType = getUpstreamType().getScaleType(platform); } } else if (getUpstreamType() != null) { scaleType = getUpstreamType().getScaleType(platform); } if (scaleType == null) { scaleType = PropertyType.NOT_APPLICABLE; } return scaleType; } @NonProperty public void setPrecisionType(String platform, PropertyType precisionType) { getOrCreatePhysicalProperties(platform).setPrecisionType(precisionType); } @NonProperty public void setScaleType(String platform, PropertyType scaleType) { getOrCreatePhysicalProperties(platform).setScaleType(scaleType); } @Mutator public void setMyDescription(String myDescription) { String oldValue = this.description; this.description = myDescription; firePropertyChange("myDescription", oldValue, description); } @Accessor public String getMyDescription() { return this.description; } @Transient @Accessor public String getDescription() { if (description == null && upstreamType != null) { return upstreamType.getDescription(); } else { return description; } } /** * Gets a {@link SQLTypePhysicalProperties} instance for the given platform * from this type's map of {@link SQLTypePhysicalProperties}. If none * exists, then create a fresh one. * * @param platform * The platform name to search physical properties for * @return Either an existing {@link SQLTypePhysicalProperties} for the * given platform, or a new one created if none existed previously */ private SQLTypePhysicalProperties getOrCreatePhysicalProperties(String platform) { SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties == null) { properties = new SQLTypePhysicalProperties(platform); properties.setName(getName()); putPhysicalProperties(platform, properties); } return properties; } @Mutator public void setUpstreamType(UserDefinedSQLType upstreamType) { begin("setting upstream type"); UserDefinedSQLType oldValue = this.upstreamType; this.upstreamType = upstreamType; firePropertyChange("upstreamType", oldValue, upstreamType); if (upstreamType != null) { for (String platform : platforms()) { SQLTypePhysicalProperties upstreamProperties = upstreamType.getPhysicalProperties(platform); if (upstreamProperties != null) { SQLTypePhysicalProperties physicalProperties = getPhysicalProperties(platform); physicalProperties.setName(upstreamProperties.getName()); if (upstreamProperties.getPrecisionType() != PropertyType.VARIABLE) { physicalProperties.setPrecision(null); physicalProperties.setPrecisionType(null); } if (upstreamProperties.getScaleType() != PropertyType.VARIABLE) { physicalProperties.setScale(null); physicalProperties.setScaleType(null); } } } // Fix/hack to make sure the type is set to the same as the upstream // type. setType(upstreamType.getType()); } commit(); } @Accessor public UserDefinedSQLType getUpstreamType() { return upstreamType; } /** * An important note of this implementation of * {@link #addChildImpl(SPObject, int)} is that it will not accept adding * {@link SQLTypePhysicalProperties} for the * {@link SQLTypePhysicalPropertiesProvider#GENERIC_PLATFORM} The * SQLTypePhysicalProperties for GENERIC_PLATFORM is always * {@link #defaultPhysicalProperties}, and it cannot ever be changed. */ public void putPhysicalProperties(String platform, SQLTypePhysicalProperties properties) { if (platform.equals(GENERIC_PLATFORM)) { throw new IllegalArgumentException( "The SQLTypePhysicalProperties object for " + GENERIC_PLATFORM + " cannot be overwritten. Instead, use getDefaultPhysicalProperties and modify its properties"); } SQLTypePhysicalProperties oldProperties = getPhysicalProperties(platform); // Add new properties overridingPhysicalProperties.add(properties); int index = overridingPhysicalProperties.indexOf(properties) + 1; properties.setParent(this); fireChildAdded(SQLTypePhysicalProperties.class, properties, index); // Remove old properties if (oldProperties != null && oldProperties != defaultPhysicalProperties) { int oldIndex = overridingPhysicalProperties.indexOf(oldProperties); overridingPhysicalProperties.remove(oldProperties); fireChildRemoved(SQLTypePhysicalProperties.class, oldProperties, oldIndex); oldProperties.setParent(null); } } /** * An important note of this implementation of * {@link #addChildImpl(SPObject, int)} is that it will not accept adding * children at index 0. Index 0 is the index of the * {@link #defaultPhysicalProperties}, and it cannot ever be moved. */ @Override protected void addChildImpl(SPObject child, int index) { // super.addChild() should already be checking for type if (child instanceof SQLTypePhysicalProperties) { SQLTypePhysicalProperties newProperties = (SQLTypePhysicalProperties) child; if (newProperties == defaultPhysicalProperties) return; if (index == 0) { throw new IllegalArgumentException( "Cannot insert child " + child.getName() + " at index 0 for " + getName() + ", " + "as this is where the default physical properties must always be."); } SQLTypePhysicalProperties oldProperties = getPhysicalProperties(newProperties.getPlatform()); // Add new properties. Insert at index - 1 is because // defaultPhysicalProperties is an always existent child at index 0. overridingPhysicalProperties.add(index - 1, newProperties); newProperties.setParent(this); fireChildAdded(SQLTypePhysicalProperties.class, newProperties, index); // Remove old properties if (oldProperties != null && oldProperties != defaultPhysicalProperties) { int oldIndex = overridingPhysicalProperties.indexOf(oldProperties); overridingPhysicalProperties.remove(oldProperties); fireChildRemoved(SQLTypePhysicalProperties.class, oldProperties, oldIndex); oldProperties.setParent(null); } } else { throw new IllegalArgumentException("Children Must be of type SQLTypePhysicalProperties"); } } @Override public String toString() { return getName(); } @Mutator public void setMyNullability(Integer nullability) { begin("Setting myNullability."); Integer oldValue = getMyNullability(); this.myNullability = nullability; firePropertyChange("myNullability", oldValue, nullability); commit(); } @Transient @Accessor public Integer getNullability() { if (myNullability == null && upstreamType != null) { return upstreamType.getNullability(); } else if (myNullability == null && upstreamType == null) { // to avoid NullPointer Exception return DatabaseMetaData.columnNullableUnknown; } else { return myNullability; } } @Accessor public Integer getMyNullability() { return myNullability; } @Mutator public void setMyAutoIncrement(Boolean autoIncrement) { begin("Setting myAutoIncrement."); Boolean oldValue = getMyAutoIncrement(); this.myAutoIncrement = autoIncrement; firePropertyChange("myAutoIncrement", oldValue, autoIncrement); commit(); } @Transient @Accessor public Boolean getAutoIncrement() { return (myAutoIncrement == null && upstreamType != null) ? upstreamType.getAutoIncrement() : myAutoIncrement; } @Accessor public Boolean getMyAutoIncrement() { return myAutoIncrement; } /** * goes through each of the UserDefinedSQLType's children appending each's * platform to the list of names * * @return a list of platform names */ public List<String> platforms() { List<String> platforms = new ArrayList<String>(); for (SQLTypePhysicalProperties properties : overridingPhysicalProperties) { platforms.add(properties.getPlatform()); } platforms.add(defaultPhysicalProperties.getPlatform()); return platforms; } /** * Compares two {@link UserDefinedSQLType} objects to see if they are equal * in all properties (except UUID). * * @param udt1 * The first of two {@link UserDefinedSQLType} objects to * compare. * @param udt2 * The second of two {@link UserDefinedSQLType} objects to * compare. * @return true iff the two {@link UserDefinedSQLType} objects are equal. */ public static boolean areEqual(UserDefinedSQLType udt1, UserDefinedSQLType udt2) { Set<String> oldPlatforms = new HashSet<String>(udt1.platforms()); Set<String> newPlatforms = new HashSet<String>(udt2.platforms()); String defaultPlatform = udt1.getDefaultPhysicalProperties().getPlatform(); boolean equal = SQLTypePhysicalProperties.areEqual(udt1.getDefaultPhysicalProperties(), udt2.getDefaultPhysicalProperties()); if (!equal) { return false; } oldPlatforms.remove(defaultPlatform); newPlatforms.remove(defaultPlatform); equal = SQLPowerUtils.areEqual(udt1.getName(), udt2.getName()) && SQLPowerUtils.areEqual(udt1.type, udt2.type) && SQLPowerUtils.areEqual(udt1.myNullability, udt2.myNullability) && SQLPowerUtils.areEqual(udt1.myAutoIncrement, udt2.myAutoIncrement) && SQLPowerUtils.areEqual(udt1.description, udt2.description) && SQLPowerUtils.areEqual(udt1.basicType, udt2.basicType) && ((udt1.getUpstreamType() == null && udt2.getUpstreamType() == null) || (udt1.getUpstreamType() != null && udt2.getUpstreamType() != null && areEqual(udt1.getUpstreamType(), udt2.getUpstreamType()))) && SQLPowerUtils.areEqual(oldPlatforms.size(), newPlatforms.size()) && oldPlatforms.containsAll(newPlatforms); if (equal) { for (String platform : oldPlatforms) { SQLTypePhysicalProperties oldProperties = udt1.getPhysicalProperties(platform); SQLTypePhysicalProperties newProperties = udt2.getPhysicalProperties(platform); if (!SQLTypePhysicalProperties.areEqual(oldProperties, newProperties)) { return false; } } } return equal; } /** * Updates each of this UserDefinedSQLType's properties to match the input * type. This includes the properties of the physical properties. The * changes are made in a single transaction. * * @param matchMe * the UserDefinedSQLType who's properties we are copying */ @Override public void updateToMatch(SQLObject matchMe) { if (!(matchMe instanceof UserDefinedSQLType)) { throw new ClassCastException("Only " + UserDefinedSQLType.class.getSimpleName() + " can be copied to " + UserDefinedSQLType.class.getSimpleName() + "."); } copyProperties(this, (UserDefinedSQLType) matchMe); } @Transient @Accessor public SQLTypePhysicalProperties getDefaultPhysicalProperties() { return defaultPhysicalProperties; } /** * Copies all non-final properties (except UUID) and children from one * {@link UserDefinedSQLType} object to another. * * @param target * The target {@link UserDefinedSQLType} to copy to. * @param source * The source {@link UserDefinedSQLType} to copy from. */ public static final void copyProperties(final UserDefinedSQLType target, final UserDefinedSQLType source) { if (!areEqual(target, source)) { target.begin("Copying UserDefinedSQLType"); target.setUpstreamType(source.getUpstreamType()); target.setName(source.getName()); target.setPhysicalName(source.getPhysicalName()); // Don't use getters as they will refer to the upstreamType if the // values are null target.setMyAutoIncrement(source.myAutoIncrement); target.setBasicType(source.basicType); target.setMyDescription(source.description); target.setMyNullability(source.myNullability); target.setType(source.type); // XXX Platform name is final. If the default platform names don't match // the source and target default platforms won't exactly be the same. SQLTypePhysicalProperties.copyProperties(target.getDefaultPhysicalProperties(), source.getDefaultPhysicalProperties()); final List<String> sourcePlatforms = new ArrayList<String>(source.platforms()); final List<String> targetPlatforms = new ArrayList<String>(target.platforms()); sourcePlatforms.remove(source.getDefaultPhysicalProperties().getPlatform()); targetPlatforms.remove(target.getDefaultPhysicalProperties().getPlatform()); Collections.sort(sourcePlatforms); Collections.sort(targetPlatforms); for (int i = 0, j = 0; i < sourcePlatforms.size() || j < targetPlatforms.size();) { int compare = 0; String sourcePlatform; if (i < sourcePlatforms.size()) { sourcePlatform = sourcePlatforms.get(i); } else { sourcePlatform = null; compare = 1; } String targetPlatform; if (j < targetPlatforms.size()) { targetPlatform = targetPlatforms.get(j); } else { targetPlatform = null; compare = -1; } if (compare == 0) { compare = sourcePlatform.compareTo(targetPlatform); } if (compare < 0) { try { target.addChild(new SQLTypePhysicalProperties(source.getPhysicalProperties(sourcePlatform))); } catch (SQLObjectException e) { target.rollback("Could not copy UserDefinedSQLType"); throw new IllegalStateException("Could not add new " + "SQLTypePhysicalProperties for platform " + sourcePlatform + " to UserDefinedSQLType " + target.getPhysicalName() + " in copyProperties."); } i++; } else if (compare > 0) { try { target.removeChild(target.getPhysicalProperties(targetPlatform)); } catch (IllegalArgumentException e) { target.rollback("Could not copy UserDefinedSQLType"); throw new IllegalStateException("Could not remove " + "SQLTypePhysicalProperties for platform " + targetPlatform + " from UserDefinedSQLType " + target.getPhysicalName()); } catch (ObjectDependentException e) { target.rollback("Could not copy UserDefinedSQLType"); throw new IllegalStateException("Could not remove " + "SQLTypePhysicalProperties for platform " + targetPlatform + " from UserDefinedSQLType " + target.getPhysicalName()); } j++; } else { target.getPhysicalProperties(targetPlatform).updateToMatch(source.getPhysicalProperties(sourcePlatform)); i++; j++; } } target.commit(); } } /** * Returns the physical name of this type under the given platform. * * @param platform * The name of the platform (usually what is returned by * {@link JDBCDataSourceType#getName()} * @return The physical name of this type for the given platform. If a * platform specific override doesn't exist for that platform, it * returns the name under the 'default' platform. */ @NonProperty public String getPhysicalName(String platform) { String physicalName = null; SQLTypePhysicalProperties properties = getPhysicalProperties(platform); if (properties != null) { physicalName = properties.getName(); if (physicalName == null && getUpstreamType() != null) { physicalName = getUpstreamType().getPhysicalName(platform); } } else if (getUpstreamType() != null) { physicalName = getUpstreamType().getPhysicalName(platform); } return physicalName; } @Override @Mutator public void setName(String name) { begin("Setting name"); super.setName(name); defaultPhysicalProperties.setName(name); commit(); } public void removeEnumeration(String platform, SQLEnumeration enumeration) { getOrCreatePhysicalProperties(platform).removeEnumeration(enumeration); } }