Package org.jpox.store.rdbms

Source Code of org.jpox.store.rdbms.Column

/**********************************************************************
Copyright (c) 2002 Kelly Grizzle (TJDO) and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
2003 Erik Bengtson - added a replaceAll operation in substitution for
                    java.lang.String.replaceAll of J2SDK 1.4. Now we
                    can compile with J2SDK 1.3.1
2003 Erik Bengtson - moved replaceAll to StringUtils class
2003 Andy Jefferson - updated the DEFAULT_MAX_STRING_LENGTH to be controllable
                     via a property.
2004 Andy Jefferson - updated the NULL/NOT NULL so that we always specify the
                     nullability of a column on creation
2004 Erik Bengtson - added columnOptions getters that can be read by
                    the SetTable or MapTable (Collection). columnOptions is
                    used for create an equivalent column on the set table
2004 Andy Jefferson - added Auto, Unique, Default, options to column SQL options
2006 Andy Jefferson - removed co, dba, lengthType, copying of ColumnMetaData
    ...
**********************************************************************/
package org.jpox.store.rdbms;

import java.sql.DatabaseMetaData;
import java.util.StringTokenizer;

import org.jpox.ClassNameConstants;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;

import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ColumnMetaData;
import org.jpox.metadata.MetaData;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreField;
import org.jpox.store.mapped.DatastoreFieldDefinitionException;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.mapped.mapping.DatastoreMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.rdbms.adapter.RDBMSAdapter;
import org.jpox.store.rdbms.columninfo.ColumnInfo;
import org.jpox.store.rdbms.exceptions.IncompatibleDataTypeException;
import org.jpox.store.rdbms.exceptions.WrongPrecisionException;
import org.jpox.store.rdbms.exceptions.WrongScaleException;
import org.jpox.store.rdbms.table.TableImpl;
import org.jpox.store.rdbms.typeinfo.TypeInfo;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.StringUtils;

/**
* Definition of a Column in the datastore.
* Contains the full definition of the column, its type, size, whether it is autoincrement,
* nullable, part of the PK etc. The SQL column definition is generated here.
*
* @version $Revision: 1.65 $
**/
public class Column implements DatastoreField
{
    /** Localisation of messages. */
    private static final Localiser LOCALISER = Localiser.getInstance("org.jpox.store.rdbms.Localisation",
        RDBMSManager.class.getClassLoader());

    private static final int PRIMARY_KEY_PART = 1;
    private static final int EXACT_PRECISION = 2;
    private static final int NULLABLE = 4;
    private static final int UNIQUE = 8;

    /** wrapper function select **/
    public static final int WRAPPER_FUNCTION_SELECT = 0;

    /** wrapper function insert **/
    public static final int WRAPPER_FUNCTION_INSERT = 1;

    /** wrapper function update **/
    public static final int WRAPPER_FUNCTION_UPDATE = 2;

    /** Identifier for the column in the datastore. */
    protected DatastoreIdentifier identifier;

    /** ColumnMetaData for this column. */
    protected ColumnMetaData columnMetaData;

    /** Table containing this column in the datastore. */
    protected final DatastoreContainerObject table;

    /** Datastore mapping for this column. */
    protected DatastoreMapping datastoreMapping = null;

    /** Java type that this column is storing. (can we just get this from the mapping above ?) */
    protected final String storedJavaType;

    /** Manager for the store into which we are persisting. */
    protected final MappedStoreManager storeMgr;

    /** Type info for the JDBC type being stored in this column */
    protected TypeInfo typeInfo;

    /** Optional constraints to apply to this column in its SQL specification. */
    protected String constraints;

    protected int flags;

    /** Default value accepted by the datastore for this column, from DatabaseMetaData. */
    protected Object defaultValue;

    /** Whether this column should be defaulted on inserts. */
    protected boolean defaultable = false;

    /** Whether this is to use AUTOINCREMENT/IDENTITY. */
    protected boolean autoIncrement = false;

    /** Function wrapping the column (for example, SQRT(COLUMN)). */
    protected String[] wrapperFunction;

    /**
     * Constructor.
     * @param table The table in the datastore that this column belongs to.
     * @param javaType The type of data being stored in this column
     * @param identifier The identifier of the column (in the datastore).
     * @param colmd The ColumnMetaData for this column
     */
    public Column(DatastoreContainerObject table, String javaType, DatastoreIdentifier identifier, ColumnMetaData colmd)
    {
        this.table = table;
        this.storedJavaType = javaType;
        this.storeMgr = table.getStoreManager();

    typeInfo = null;
    constraints = null;
    flags = 0;

        setIdentifier(identifier);
        if (colmd == null)
        {
            // Create a default ColumnMetaData since none provided
            columnMetaData = new ColumnMetaData(null, (String) null);
        }
        else
        {
            // TODO Consider making a copy here
            columnMetaData = colmd;
        }

        // Nullability
        if (columnMetaData.isAllowsNullSet() && columnMetaData.isAllowsNull())
        {
            // MetaData requires it to be nullable
            setNullable();
        }

        // Uniqueness
        if (columnMetaData.getUnique())
        {
            // MetaData requires it to be unique
            setUnique();
        }

        wrapperFunction = new String[3];
    wrapperFunction[WRAPPER_FUNCTION_SELECT]= "?";
    wrapperFunction[WRAPPER_FUNCTION_INSERT]= "?";
    wrapperFunction[WRAPPER_FUNCTION_UPDATE]= "?";
    }

    /**
     * Convenience method to check if the length is required to be unlimited (BLOB/CLOB).
     * @return Whether unlimited length required.
     */
    public boolean isUnlimitedLength()
    {
        if (columnMetaData.getJdbcType() != null && columnMetaData.getJdbcType().toLowerCase().indexOf("lob") > 0)
        {
            // Allow for jdbc-type=BLOB/CLOB
            return true;
        }
        else if (columnMetaData.getSqlType() != null && columnMetaData.getSqlType().toLowerCase().indexOf("lob") > 0)
        {
            // Allow for sql-type=BLOB/CLOB
            return true;
        }
        return false;
    }

    /**
     * Accessor for the identifier of the datastore column.
     * @return The identifier of the column in the datastore.
     */
    public DatastoreIdentifier getIdentifier()
    {
        return identifier;
    }

    /**
     * Mutator for the identifier of the column.
     * This is only required when we are overriding details of the column, during the initialisation process.
     * @param identifier The identifier
     */
    public void setIdentifier(DatastoreIdentifier identifier)
    {
        this.identifier = identifier;
    }

    /**
     * Accessor for the DatastoreObject. The table owning this Column.
     * @return The DatastoreObject aka the table
     */
    public DatastoreContainerObject getDatastoreContainerObject()  
    {
        return table;
    }

    /**
     * Accessor for the datastore mapping that this datastore field relates to.
     * @return The datastore mapping
     */
    public DatastoreMapping getDatastoreMapping()
    {
        return datastoreMapping;
    }

    /**
     * Method to associate this datastore field with its mapping.
     * @param mapping The mapping for this datastore field
     */
    public void setDatastoreMapping(DatastoreMapping mapping)
    {
        datastoreMapping = mapping;
    }

    /**
     * Accessor for the Mapping.
     * @return The Mapping
     */
    public JavaTypeMapping getMapping()
    {
        return datastoreMapping.getJavaTypeMapping();
    }

    /**
     * Accessor for the type of data stored in this column.
     * @return The type of data in the column
     */
    public String getStoredJavaType()
    {
        return storedJavaType;
    }

    /**
     * Accessor for the type info for this column.
     * @return The type info
     */
    public final TypeInfo getTypeInfo()
    {
        return typeInfo;
    }

    /**
     * Accessor for the JDBC type being used for this Column
     * @return The JDBC data type
     */
    public int getJdbcType()
    {
        return typeInfo.dataType;
    }
   
    /**
     * Accessor for the Store Manager.
     * @return Store Manager
     **/
    public MappedStoreManager getStoreManager()
    {
        return storeMgr;
    }

    /**
     * Accessor for the precision of data in the column.
     * @return The precision of data in the column
     **/
    private int getSQLPrecision()
    {
        int sqlPrecision = - 1;

        if (isUnlimitedLength())
        {
            int ulpv = ((RDBMSAdapter) storeMgr.getDatastoreAdapter()).getUnlimitedLengthPrecisionValue(typeInfo);
            if (ulpv > 0)
            {
                sqlPrecision = ulpv;
            }
        }
        else
        {
            if (columnMetaData.getLength() != null)
            {
                sqlPrecision = columnMetaData.getLength().intValue();
            }
        }

        // Databases like Derby that use BIT types for binary need to have the length expressed in bits, not bytes.
        if (typeInfo.typeName.toLowerCase().startsWith("bit"))
        {
            return sqlPrecision * 8;
        }
        else
        {
            return sqlPrecision;
        }
    }

    /**
     * Accessor for the SQL definition of this column.
     * @return The SQL definition of the column
     **/
    public String getSQLDefinition()
    {
        StringBuffer def = new StringBuffer(identifier.toString());
        StringBuffer typeSpec = new StringBuffer(typeInfo.typeName);
        RDBMSAdapter adapter = (RDBMSAdapter) storeMgr.getDatastoreAdapter();

        // Add type specification.
        boolean specifyType = true;
        if (adapter.supportsIdentityFields() && autoIncrement && !adapter.supportsAutoIncrementColumnTypeSpecification())
        {
            specifyType = false;
        }

        if (specifyType)
        {
            // Parse and append createParams to the typeName if it looks like it's supposed to be appended,
            // i.e. if it contains parentheses, and the type name itself doesn't. createParams is mighty
            // ill-defined by the JDBC spec, but attempt to interpret it.
            if (typeInfo.createParams != null && typeInfo.createParams.indexOf('(') >= 0 && typeInfo.typeName.indexOf('(') < 0)
            {
                StringTokenizer toks = new StringTokenizer(typeInfo.createParams);
               
                while (toks.hasMoreTokens())
                {
                    String tok = toks.nextToken();
                   
                    if (tok.startsWith("[") && tok.endsWith("]"))
                    {
                        // The brackets look like they indicate an optional param so
                        // skip
                        continue;
                    }
                   
                    typeSpec.append(" " + tok);
                }
            }
           
            // Add any precision - note that there is no obvious flag in the JDBC typeinfo to
            // tell us whether the type allows this specification or not, and many JDBC just
            // return crap anyway. We use the allowsPrecisionSpec flag for this
            StringBuffer precSpec = new StringBuffer();
            int sqlPrecision = getSQLPrecision();
            if (sqlPrecision > 0 && typeInfo.allowsPrecisionSpec)
            {
                precSpec.append(sqlPrecision);
                if (columnMetaData.getScale() != null)
                {
                    precSpec.append("," + columnMetaData.getScale());
                }
            }
            else if (sqlPrecision > 0 && !typeInfo.allowsPrecisionSpec)
            {
                JPOXLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("020183", this.toString()));
            }
            int lParenIdx = typeSpec.toString().indexOf('(');
            int rParenIdx = typeSpec.toString().indexOf(')', lParenIdx);
            if (lParenIdx > 0 && rParenIdx > 0)
            {
                // Some databases (like DB2) give you typeNames with ()'s already
                // present ready for you to insert the values instead of appending them.
                if (precSpec.length() > 0)
                {
                    typeSpec.replace(lParenIdx + 1, rParenIdx, precSpec.toString());
                }
                else if (rParenIdx == lParenIdx + 1)
                {
                    throw new DatastoreFieldDefinitionException(LOCALISER.msg("020184", this.toString()));
                }
            }
            else if (precSpec.length() > 0)
            {
                typeSpec.append('(');
                typeSpec.append(precSpec.toString());
                typeSpec.append(')');
            }
            def.append(" " + typeSpec.toString());
        }

        // Add DEFAULT (if specifiable before NULL)
        if (adapter.supportsDefaultBeforeNullInColumnOptions() &&
            adapter.supportsDefaultKeywordInColumnOptions() &&
            columnMetaData.getDefaultValue() != null)
        {
            def.append(" ").append(getDefaultDefinition());
        }

        // Nullability
        if (adapter.supportsIdentityFields() && autoIncrement && !adapter.supportsAutoIncrementKeysNullSpecification())
        {
            // Do nothing since the adapter doesn't allow NULL specifications with autoincrement/identity
        }
        else
        {
            if (!isNullable())
            {
                if (columnMetaData.getDefaultValue() == null || adapter.supportsDefaultKeywordWithNotNullInColumnOptions())
                {
                    def.append(" NOT NULL");
                }
            }
            else if (typeInfo.nullable == DatabaseMetaData.columnNullable)
            {
                if (adapter.supportsNullsKeywordInColumnOptions())
                {
                    def.append(" NULL");
                }
            }
        }

        // Add DEFAULT (if specifiable after NULL)
        if (!adapter.supportsDefaultBeforeNullInColumnOptions() &&
            adapter.supportsDefaultKeywordInColumnOptions() &&
            columnMetaData.getDefaultValue() != null)
        {
            def.append(" ").append(getDefaultDefinition());
        }

        // Constraints checks
        if (adapter.supportsCheckInCreateStatements() && constraints != null)
        {
            def.append(" " + constraints.toString());
        }

        // Auto Increment
        if (adapter.supportsIdentityFields() && autoIncrement)
        {
            def.append(" " + adapter.getAutoIncrementKeyword());
        }

        // Uniqueness
        if (isUnique() && !adapter.supportsUniqueConstraintsInEndCreateStatements())
        {
            def.append(" UNIQUE");
        }

        return def.toString();
    }

    /**
     * Convenience method to return the "DEFAULT" part of the column definition.
     * @return The default part of the column definition.
     */
    private String getDefaultDefinition()
    {
        // TODO Allow for user specification of "null" to symbolise "DEFAULT NULL"

        // Quote any character types (CHAR, VARCHAR, BLOB, CLOB)
        if (typeInfo.typeName.toUpperCase().indexOf("CHAR") >= 0 ||
            typeInfo.typeName.toUpperCase().indexOf("LOB") >= 0)
        {
            // NOTE We use single quote here but would be better to take
            // some character from the DatabaseMetaData. The "identifierQuoteString"
            // does not work for string quoting here for Postgres
            return "DEFAULT '" + columnMetaData.getDefaultValue() + "'";
        }
        else if (typeInfo.typeName.toUpperCase().indexOf("BIT") == 0)
        {
            if (columnMetaData.getDefaultValue().equalsIgnoreCase("true") || columnMetaData.getDefaultValue().equalsIgnoreCase("false"))
            {
                // Quote any "true"/"false" values for BITs
                return "DEFAULT '" + columnMetaData.getDefaultValue() + "'";
            }
        }
        return "DEFAULT " + columnMetaData.getDefaultValue();
    }

    /**
     * Initialize the default column value and auto increment
     * @param ci The column information
     */
    public void initializeColumnInfoFromDatastore(ColumnInfo ci)
    {
        String column_default = ci.getColumnDef();
        if (getStoredJavaType() != null && getStoredJavaType().equals(ClassNameConstants.JAVA_LANG_STRING))
        {
            if (column_default != null)
            {
                setDefaultValue(StringUtils.replaceAll(
                    StringUtils.replaceAll(
                        StringUtils.replaceAll(
                            StringUtils.replaceAll(column_default, "'", ""), "\"", ""),
                    ")", ""), "(", ""));
            }
        }
        else
        {
            if (column_default != null)
            {
                setDefaultValue(StringUtils.replaceAll(
                    StringUtils.replaceAll(
                        StringUtils.replaceAll(
                            StringUtils.replaceAll(column_default, "'", ""), "\"", ""),
                    ")", ""), "(", ""));
            }
        }

        // TODO Make sure that this lines up with the defaultValue when set.
        try
        {
            autoIncrement = storeMgr.getDatastoreAdapter().isIdentityFieldDataType(ci.getColumnDef());
        }
        catch( UnsupportedOperationException ex)
        {
            //do nothing, or maybe log
        }
    }

    /**
     * Method to validate the contents of the column. This method can throw
     * IncompatibleDataTypeException, WrongScaleException,
     * WrongPrecisionException, IsNullableException if the data in the column is
     * not compatible with the supplied ColumnInfo.
     * @param ci The column information taken from the database
     */
    public void validate(ColumnInfo ci)
    {
        if (!typeInfo.isCompatibleWith(ci))
        {
            throw new IncompatibleDataTypeException(this, typeInfo.dataType, ci.getDataType());
        }
        if (!typeInfo.isToValidate(ci))
        {
            return;
        }
        if (table instanceof TableImpl)
        {
            // Only validate precision/scale/nullability for tables
            if (typeInfo.allowsPrecisionSpec)
            {
                int actualPrecision = ci.getColumnSize();
                int actualScale = ci.getDecimalDigits();
                int sqlPrecision = getSQLPrecision();
               
                if (sqlPrecision > 0 && actualPrecision > 0)
                {
                    if (sqlPrecision != actualPrecision)
                    {
                        if( this.columnMetaData != null && this.columnMetaData.getParent() != null && (this.columnMetaData.getParent() instanceof AbstractMemberMetaData))
                        {
                            //includes the field name in error msg
                            throw new WrongPrecisionException(this.toString(), sqlPrecision, actualPrecision, ((AbstractMemberMetaData)this.columnMetaData.getParent()).getFullFieldName());
                        }
                        else
                        {
                            throw new WrongPrecisionException(this.toString(), sqlPrecision, actualPrecision);
                        }
                    }
                }
               
                if (columnMetaData.getScale() != null && actualScale >= 0)
                {
                    if (columnMetaData.getScale().intValue() != actualScale)
                    {
                        if( this.columnMetaData != null && this.columnMetaData.getParent() != null && (this.columnMetaData.getParent() instanceof AbstractMemberMetaData))
                        {
                            //includes the field name in error msg
                            throw new WrongScaleException(this.toString(), columnMetaData.getScale().intValue(), actualScale, ((AbstractMemberMetaData)this.columnMetaData.getParent()).getFullFieldName());
                        }
                        else
                        {
                            throw new WrongScaleException(this.toString(), columnMetaData.getScale().intValue(), actualScale);
                        }
                    }
                }
            }

            String actualIsNullable = ci.getIsNullable();
            if (actualIsNullable.length() > 0)
            {
                switch (Character.toUpperCase(actualIsNullable.charAt(0)))
                {
                    case 'Y' :
                        if (!isNullable())
                        {
                            JPOXLogger.DATASTORE.warn(LOCALISER.msg("020025", this));
                        }
                        break;

                    case 'N' :
                        // TODO convert to default value somewhere at runtime if
                        // column has a default value and "default"
                        break;

                    default :
                        break;
                }
            }
           
            try
            {
                if (isAutoIncrement() != storeMgr.getDatastoreAdapter().isIdentityFieldDataType(ci.getColumnDef()))
                {
                    //TODO localise
                    if (isAutoIncrement())
                    {
                        throw new JPOXException("Expected an auto increment column ("+getIdentifier()+") in the database, but it is not").setFatal();
                    }
                    else
                    {
                        throw new JPOXException("According to the user metadata, the column ("+getIdentifier()+") is not auto incremented, but the database says it is.").setFatal();
                    }
                }
            }
            catch (UnsupportedOperationException ex)
            {
                //ignore this validation step
            }
        }
    }

    /**
     * Mutator for the type information of the column.
     * @param typeInfo The type info
     * @return The column with the updated info
     */
    public final Column setTypeInfo(TypeInfo typeInfo)
    {
    if (this.typeInfo == null)
    {
      this.typeInfo = typeInfo;
    }
        return this;
    }

    /**
     * Mutator for the constraints of the column.
     *
     * @param constraints The constraints
     * @return The column with the updated info
     **/
    public final Column setConstraints(String constraints)
    {
        this.constraints = constraints;
        return this;
    }

    /**
     * Mutator to make the column (part of) the primary key
     **/
    public final void setAsPrimaryKey()
    {
        flags |= PRIMARY_KEY_PART;
        //primary keys cannot be null
        flags &= ~NULLABLE;
    }

    /**
     * Mutator for the nullability of the column.
     * @return The column with the updated info
     **/
    public final DatastoreField setNullable()
    {
        flags |= NULLABLE;
        return this;
    }

    /**
     * Mutator for the defaultability of the column.
     * @return The column with the updated info
     **/
    public final DatastoreField setDefaultable()
    {
        defaultable = true;
        return this;
    }

    /**
     * Mutator for the uniqueness of the column.
     * @return The column with the updated info
     **/
    public final Column setUnique()
    {
        flags |= UNIQUE;
        return this;
    }

    /**
     * Accessor for whether the column is the primary key.
     * @return whether the column is (part of) the primary key
     **/
    public final boolean isPrimaryKey()
    {
        return ((flags & PRIMARY_KEY_PART) != 0);
    }

    /**
     * Accessor for whether the column has exact precision.
     * @return whether the column has exact precision
     **/
    public final boolean isExactPrecision()
    {
        return ((flags & EXACT_PRECISION) != 0);
    }

    /**
     * Accessor for whether the column is nullable.
     * @return whether the column is nullable
     **/
    public final boolean isNullable()
    {
        return ((flags & NULLABLE) != 0);
    }

    /**
     * Accessor for whether the column is defaultable.
     * @return whether the column is defaultable
     **/
    public final boolean isDefaultable()
    {
        return (defaultable);
    }

    /**
     * Accessor for whether the column is unique.
     * @return whether the column is unique
     **/
    public final boolean isUnique()
    {
        return ((flags & UNIQUE) != 0);
    }

    /**
     * Equality method.
     * @param obj The Object to compare against
     * @return Whether they are considered equal.
     */
    public boolean equals(Object obj)
    {
       if (obj == this)
        {
            return true;
        }

        if (!(obj instanceof Column))
        {
            return false;
        }

        Column col = (Column)obj;
        return table.equals(col.table) && identifier.equals(col.identifier);
    }

    /**
     * Accessor for the hashcode.
     * @return The hashcode
     */
    public int hashCode()
    {
        return table.hashCode() ^ identifier.hashCode();
    }

    /**
     * Accessor for a string form of this Column.
     * Returns a fully-qualified form of the column name (including table prefix).
     * @return String form of the Column
     */
    public String toString()
    {
        return table.toString() + "." + identifier;
    }

    /**
     * Wraps the column name with a FUNCTION. <PRE>example: SQRT(?) generates: SQRT(columnName)</PRE>
     * @param replacementValue the replacement to ?. Probably it's a column name, that may be fully qualified name or not
     * @return a String with function taking as parameter the replacementValue
     */
    public String applySelectFunction(String replacementValue)
    {
        if (replacementValue == null)
        {
            return wrapperFunction[WRAPPER_FUNCTION_SELECT];
        }
        if (wrapperFunction[WRAPPER_FUNCTION_SELECT] != null)
        {
            return StringUtils.replaceAll(wrapperFunction[WRAPPER_FUNCTION_SELECT], "?", replacementValue);
        }
        return replacementValue;
    }   

    /**
     * Accessor for the default Value
     * @return the default value
     */
    public Object getDefaultValue()
    {
        return defaultValue;
    }

    /**
     * Mutator for the default Value
     * @param object default value
     */
    public void setDefaultValue(Object object)
    {
        defaultValue = object;
    }

    /**
     * Accessor for the columnOptions
     * @return The Column Options (length, scale, precision etc)
     */
    public final ColumnMetaData getColumnMetaData()
    {
        return columnMetaData;
    }
   
    /**
     * Access the metadata definition defining this DatastoreField.
     * For RDBMS this will return an instance of ColumnMetaData.
     * @return the MetaData
     */
    public MetaData getMetaData()
    {
        return columnMetaData;
    }

    /**
     * Accessor for the MetaData of the field that this is the datastore field for.
     * @return MetaData of the field (if representing a field of a class).
     */
    public AbstractMemberMetaData getFieldMetaData()
    {
        if (columnMetaData != null && columnMetaData.getParent() instanceof AbstractMemberMetaData)
        {
            return (AbstractMemberMetaData)columnMetaData.getParent();
        }
        return null;
    }

    /**
     * Method to update the MetaData for this datastore field.
     * Only called before initialisation has completed and is called when overriding the definition
     * of an already created column.
     * @param md The MetaData
     */
    public void setMetaData(MetaData md)
    {
        if (md == null)
        {
            // Nothing to do since no definition of requirements
            return;
        }

        ColumnMetaData colmd = (ColumnMetaData)md;
        if (colmd.getJdbcType() != null)
        {
            columnMetaData.setJdbcType(colmd.getJdbcType());
        }
        if (colmd.getSqlType() != null)
        {
            columnMetaData.setSqlType(colmd.getSqlType());
        }
        if (colmd.getName() != null)
        {
            columnMetaData.setName(colmd.getName());
        }
        if (colmd.isAllowsNullSet())
        {
            columnMetaData.setAllowsNull(new Boolean(colmd.isAllowsNull()));
        }
        if (colmd.getLength() != null)
        {
            columnMetaData.setLength(colmd.getLength());
        }
        if (colmd.getScale() != null)
        {
            columnMetaData.setScale(colmd.getScale());
        }

        if (colmd.isAllowsNullSet() && colmd.isAllowsNull())
        {
            // MetaData requires it to be nullable
            setNullable();
        }
        if (colmd.getUnique())
        {
            // MetaData requires it to be unique
            setUnique();
        }
    }

    /**
     * @return Returns the constraints.
     */
    public String getConstraints()
    {
        return constraints;
    }
   
    /**
     * Accessor for the AutoIncrement. Either set in the metadata, or retrieved
     * from the DatabaseMetadata when validated
     * @return true if column is autoincrement
     */
    public boolean isAutoIncrement()
    {
        return autoIncrement;
    }

    /**
     * Mutator for whether we set this column as autoIncrement/identity.
     * @param auto_increment True if column is autoincrement/identity
     */
    public void setAutoIncrement(boolean auto_increment)
    {
        autoIncrement = auto_increment;
    }

    /**
     * Checks the column definition as a primitive.
     * @throws DatastoreFieldDefinitionException
     */
    public final void checkPrimitive() throws DatastoreFieldDefinitionException
    {
    }

    /**
     * Checks the column definition as an integer.
     * @throws DatastoreFieldDefinitionException
     */
    public final void checkInteger() throws DatastoreFieldDefinitionException
    {
    }

    /**
     * Checks the column definition as a decimal.
     * @throws DatastoreFieldDefinitionException
     */
    public final void checkDecimal() throws DatastoreFieldDefinitionException
    {
    }

    /**
     * Checks the column definition as a string.
     * @throws DatastoreFieldDefinitionException
     */
    public final void checkString() throws DatastoreFieldDefinitionException
    {
        if (columnMetaData.getJdbcType() == null)
        {
            columnMetaData.setJdbcType("VARCHAR");
        }
        if (columnMetaData.getLength() == null)
        {
            // Use the default string length
            columnMetaData.setLength(storeMgr.getOMFContext().getPersistenceConfiguration().getIntProperty("org.jpox.rdbms.stringDefaultLength"));
        }
    }

  public void copyConfigurationTo(DatastoreField field)
    {
        Column col = (Column) field;
        col.typeInfo = this.typeInfo;
        col.flags |= this.flags;
        col.flags &= ~PRIMARY_KEY_PART;
        col.flags &= ~UNIQUE;
        col.flags &= ~NULLABLE;
        col.defaultValue = this.defaultValue;
        col.wrapperFunction = this.wrapperFunction;

        // Copy key aspects of ColumnMetaData across. May need to copy other parts in future
        if (this.columnMetaData.getJdbcType() != null)
        {
            col.columnMetaData.setJdbcType(this.columnMetaData.getJdbcType());
        }
        if (this.columnMetaData.getSqlType() != null)
        {
            col.columnMetaData.setSqlType(this.columnMetaData.getSqlType());
        }
        if (this.columnMetaData.getLength() != null)
        {
            col.getColumnMetaData().setLength(this.columnMetaData.getLength());
        }
        if (this.columnMetaData.getScale() != null)
        {
            col.getColumnMetaData().setScale(this.getColumnMetaData().getScale());
        }
    }

    /**
     * Sets a function to wrap the column.
     * The wrapper function String must use ? to be replaced later by the column name.
     * For example
     * <pre>SQRT(?) generates: SQRT(COLUMN)</pre>
     * @param wrapperFunction The wrapperFunction to set.
     * @param wrapperMode whether select, insert or update
     */
    public void setWrapperFunction(String wrapperFunction, int wrapperMode)
    {
        if (wrapperFunction != null && wrapperMode == WRAPPER_FUNCTION_SELECT && wrapperFunction.indexOf("?") < 0)
        {
            throw new JPOXUserException("Wrapping function must have one ? (question mark). e.g. SQRT(?)");
        }
        this.wrapperFunction[wrapperMode] = wrapperFunction;
    }

    /**
     * Gets the wrapper for parameters
     * @param wrapperMode whether select, insert or update
     * @return Returns the wrapperFunction.
     */
    public String getWrapperFunction(int wrapperMode)
    {
        return wrapperFunction[wrapperMode];
    }
}
TOP

Related Classes of org.jpox.store.rdbms.Column

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.