blob: a86c1e6e8dd37c2e5067b903bfcc9369c577abca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.empire.db;
import java.sql.Connection;
import org.apache.empire.commons.Attributes;
import org.apache.empire.commons.ObjectUtils;
import org.apache.empire.commons.OptionEntry;
import org.apache.empire.commons.Options;
import org.apache.empire.data.Column;
import org.apache.empire.data.DataType;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.InvalidPropertyException;
import org.apache.empire.exceptions.NotSupportedException;
import org.apache.empire.exceptions.PropertyReadOnlyException;
import org.apache.empire.xml.XMLUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
/**
* This class represent one column of a table.
* It contains all properties of this columns (e.g. the column width).
*
*
*/
public class DBTableColumn extends DBColumn
{
// *Deprecated* private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(DBTableColumn.class);
// Additional Attributes
public static final String DBCOLATTR_SINGLEBYTECHARS = "singleByteChars";
// Column Information
protected DataType type;
protected double size;
protected boolean required;
protected boolean autoGenerated;
protected boolean readOnly;
protected Object defaultValue;
protected int decimalScale = 0;
/**
* Constructs a DBTableColumn object set the specified parameters to this object.
*
* @param table the table object to add the column to, set to null if you don't want it added to a table
* @param type the type of the column e.g. integer, text, date
* @param name the column name
* @param size the column width
* @param required flag whether the column is required
* @param autoGenerated flag whether the column is auto generated
* @param defValue the object value
*/
public DBTableColumn(DBTable table, DataType type, String name, double size, boolean required, boolean autoGenerated, Object defValue)
{
super(table, name);
// check properties
// set column properties
this.type = type;
this.required = required;
this.autoGenerated = autoGenerated;
this.readOnly = autoGenerated;
this.defaultValue = defValue;
// xml
this.attributes = new Attributes();
this.options = null;
// size (after attributes!)
setSize(size);
}
/**
* Clone Constructor - use clone()
*/
protected DBTableColumn(DBTable newTable, DBTableColumn other)
{
super(newTable, other.name);
// Copy
this.type = other.type;
this.size = other.size;
this.required = other.required;
this.autoGenerated = other.autoGenerated;
this.readOnly = other.readOnly;
this.defaultValue = other.defaultValue;
this.attributes = new Attributes();
this.attributes.addAll(other.attributes);
this.options = other.options;
if (newTable != null)
{
newTable.addColumn(this);
}
}
/**
* Returns the default column value.
* For columns of type DBDataType.AUTOINC this is assumed to be the name of a sequence
*
* @return the default column value
*/
public Object getDefaultValue()
{
return defaultValue;
}
/**
* Sets the default column value.
*
* @param defValue the default column value
*/
public void setDefaultValue(Object defValue)
{
this.defaultValue = defValue;
}
/**
* Returns the default column value.
* Unlike getDefaultValue this function is used when creating or adding records.
* If the column value is DBDataType AUTOIN this function will return a new sequence value for this record
*
* @param conn a valid database connection
* @return the default column value
*/
public Object getRecordDefaultValue(Connection conn)
{ // Check params
if (rowset==null)
return defaultValue;
// Detect default value
if (isAutoGenerated())
{ // If no connection is supplied defer till later
if (conn==null)
return null; // Create Later
// Other auto-generated values
DBDatabase db = rowset.getDatabase();
return db.getDbms().getColumnAutoValue(db, this, conn);
}
// Normal value
return defaultValue;
}
/**
* Returns the data type.
*
* @return the data type
*/
@Override
public DataType getDataType()
{
return type;
}
/**
* Gets the the column width.
*
* @return the column width
*/
@Override
public double getSize()
{
return size;
}
/**
* Changes the size of the table column<BR>
* Use for dynamic data model changes only.<BR>
* @param size the new column size
*/
public void setSize(double size)
{
// Negative size?
if (size<0)
{ // For Text-Columns set attribute "SINGLEBYTECHARS"
if (getDataType().isText())
{
setAttribute(DBCOLATTR_SINGLEBYTECHARS, Boolean.TRUE);
}
else
throw new InvalidArgumentException("size", size);
// Remove sign
size = Math.abs(size);
}
else if (attributes!=null && attributes.contains(DBCOLATTR_SINGLEBYTECHARS))
{ // Remove single by chars attribute
attributes.remove(DBCOLATTR_SINGLEBYTECHARS);
}
// set now
this.size = size;
// set scale
if (getDataType()==DataType.DECIMAL)
{ // set scale from size
int reqPrec = (int)size;
this.decimalScale = ((int)(size*10)-(reqPrec*10));
}
}
/**
* Returns the scale of the Decimal or 0 if the DataType is not DataType.DECIMAL.
* @return the decimal scale
*/
public int getDecimalScale()
{
return this.decimalScale;
}
/**
* Sets the scale of a decimal. The DataType must be set to DataType.DECIMAL otherwise an exception is thrown.
*/
public void setDecimalScale(int scale)
{
if (getDataType()!=DataType.DECIMAL)
throw new NotSupportedException(this, "setDecimalScale");
// return scale
this.decimalScale = scale;
}
/**
* Returns true if column is mandatory. Only for the graphic presentation.
*
* @return true if column is mandatory
*/
@Override
public boolean isRequired()
{
return this.required;
}
/**
* Returns true if column is a numeric sequence or otherwise generated value
*
* @return true if column is auto increment
*/
@Override
public boolean isAutoGenerated()
{
return this.autoGenerated;
}
/**
* Returns true if column the column is a single byte text or character column or false otherwise
*
* @return true if column is a single byte character based column
*/
public boolean isSingleByteChars()
{
if (attributes==null || !attributes.contains(DBCOLATTR_SINGLEBYTECHARS))
return false;
// Check Attribute
return ObjectUtils.getBoolean(attributes.get(DBCOLATTR_SINGLEBYTECHARS));
}
/**
* sets whether this column is a single byte character or text column
*/
public void setSingleByteChars(boolean singleByteChars)
{
if (!getDataType().isText())
throw new NotSupportedException(this, "setSingleByteChars");
// set single byte
setAttribute(DBCOLATTR_SINGLEBYTECHARS, singleByteChars);
}
/**
* Changes the required property of the table column<BR>
* Use for dynamic data model changes only.<BR>
* @param required true if the column is required or false otherwise
*/
public void setRequired(boolean required)
{
if (isAutoGenerated())
{ // cannot change auto-generated columns
throw new PropertyReadOnlyException("required");
}
else
{ // Set DataMode
this.required = required;
}
}
/**
* Checks whether the column is read only.
*
* @return true if the column is read only
*/
@Override
public boolean isReadOnly()
{
return this.readOnly;
}
/**
* Sets the read only attribute of the column.
*
* @param readOnly true if the column should be read only or false otherwise
*/
public void setReadOnly(boolean readOnly)
{
this.readOnly = readOnly;
}
/**
* sets the options from an enum class
*/
public void setEnumOptions(Class<?> enumType)
{
// Enum special treatment
log.debug("Adding enum options of type {} for column {}.", enumType.getName(), getName());
this.options = new Options(enumType);
// set enumType
setAttribute(Column.COLATTR_ENUMTYPE, enumType);
// check length
if (getDataType().isNumeric())
return; // no check required
int maxLength = (int)size;
for (OptionEntry oe : options)
{ // check length
String val = oe.getValueString();
if (val!=null && val.length()>maxLength)
throw new InvalidPropertyException(enumType.getName(), val);
}
}
/**
* Checks whether the supplied value is valid for this column.
* If the type of the value supplied does not match the columns
* data type the value will be checked for compatibility.
*
* @param value the checked to check for validity
* @return true if the value is valid or false otherwise.
*/
@Override
public Object validateValue(Object value)
{
return ((DBTable)rowset).validateValue(this, value);
}
/**
* Creates a foreign key relation for this column.
*
* @param target the referenced primary key column
* @return the reference object
*/
public DBRelation.DBReference referenceOn(DBTableColumn target)
{
return new DBRelation.DBReference(this, target);
}
/**
* Sets field elements, default attributes and all options to
* the specified Element object (XML tag).
*
* @param parent the parent object
* @param flags a long value
* @return the work up Element object
*/
@Override
public Element addXml(Element parent, long flags)
{ // Add Field element
Element elem = XMLUtil.addElement(parent, "column");
elem.setAttribute("name", name);
// set default attributes
DBIndex primaryKey = ((DBTable) rowset).getPrimaryKey();
if (primaryKey != null)
{
int keyIndex;
if ((keyIndex = ((DBTable) rowset).getPrimaryKey().getColumnPos(this)) >= 0)
elem.setAttribute("key", String.valueOf(keyIndex + 1));
}
if (size > 0)
{
elem.setAttribute("size", String.valueOf((int)size));
if (getDataType()==DataType.DECIMAL)
elem.setAttribute("decimals", String.valueOf((int)(size*10)%10));
}
if (isRequired())
elem.setAttribute("mandatory", String.valueOf(Boolean.TRUE));
// add All Attributes
if (attributes!=null)
attributes.addXml(elem, flags);
// add All Options
if (options!=null)
options.addXml(elem, this.type);
// done
return elem;
}
}