blob: 8a1a8c7fdd903252df06b420b6ec5058cc89eaf3 [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.beans.PropertyDescriptor;
// XML
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.empire.commons.ObjectUtils;
import org.apache.empire.commons.Options;
import org.apache.empire.commons.StringUtils;
import org.apache.empire.data.Column;
import org.apache.empire.data.ColumnExpr;
import org.apache.empire.data.RecordData;
import org.apache.empire.db.context.DBContextAware;
import org.apache.empire.db.exceptions.FieldIllegalValueException;
import org.apache.empire.exceptions.BeanPropertySetException;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.ItemNotFoundException;
import org.apache.empire.exceptions.PropertyReadOnlyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* This class provides access to the fields of one data-row of a table, view or query
* The fields can be accessed either by Column or by index
* There are various accessor functions for many data types.
* The field values are converted to the desired type if possible.
* The field values can be transferred to a classical Java-bean (aka Pojo)
*/
public abstract class DBRecordData extends DBObject
implements DBContextAware, RecordData
{
// *Deprecated* private static final long serialVersionUID = 1L;
// Logger
private static final Logger log = LoggerFactory.getLogger(DBRecordData.class);
// Field Info
@Override
public abstract int getFieldCount();
@Override
public abstract int getFieldIndex(ColumnExpr column);
@Override
public abstract int getFieldIndex(String column);
// xml
public abstract int addXmlMeta(Element parent);
public abstract int addXmlData(Element parent);
public abstract Document getXmlDocument();
/**
* Returns a value based on an index.
*/
@Override
public abstract Object getValue(int index);
/**
* Deprecated Renamed to get(...)
*/
@Deprecated
public Object getValue(ColumnExpr column)
{
return get(column);
}
/**
* Deprecated Renamed to get(...)
*/
@Deprecated
public final <T> T getValue(Column column, Class<T> returnType)
{
return get(column, returnType);
}
/**
* Deprecated Renamed to get(...)
*/
@Deprecated
public final Object[] getValues(ColumnExpr... columns)
{
return getArray(columns);
}
/**
* Returns a data value for the desired column .
*
* @param column the column for which to obtain the value
* @return the record value
*/
@Override
public final Object get(ColumnExpr column)
{
int index = getFieldIndex(column);
if (index<0)
throw new ItemNotFoundException(column.getName());
return getValue(index);
}
/**
* Returns the value of a field as an object of a given (wrapper)type
* @param column the column for which to retrieve the value
* @param returnType the type of the returned value
* @return the value
*/
public final <T> T get(Column column, Class<T> returnType)
{
return ObjectUtils.convert(returnType, get(column));
}
/**
* Returns an array of values for the given column expressions
*
* @param columns the column expressions
* @return the corresponding record values
*/
public final Object[] getArray(ColumnExpr... columns)
{
Object[] values = new Object[columns.length];
for (int i=0; i<columns.length; i++)
{
int index = getFieldIndex(columns[i]);
if (index<0)
throw new ItemNotFoundException(columns[i].getName());
values[i] = getValue(index);
}
return values;
}
/**
* Returns a data value identified by the column index.
* The value is converted to integer if necessary .
*
* @param index index of the column
* @return the record value
*/
public int getInt(int index)
{
return ObjectUtils.getInteger(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to integer if necessary.
*
* @param column identifying the column
* @return the value
*/
public final int getInt(ColumnExpr column)
{
return getInt(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to a long if necessary.
*
* @param index index of the column
* @return the value
*/
public long getLong(int index)
{
return ObjectUtils.getLong(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to a long if necessary.
*
* @param column identifying the column
* @return the value
*/
public final long getLong(ColumnExpr column)
{
return getLong(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to double if necessary.
*
* @param index index of the column
* @return the value
*/
public double getDouble(int index)
{
return ObjectUtils.getDouble(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to double if necessary.
*
* @param column identifying the column
* @return the value
*/
public final double getDouble(ColumnExpr column)
{
return getDouble(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to double if necessary.
*
* @param index index of the column
* @return the value
*/
public BigDecimal getDecimal(int index)
{
return ObjectUtils.getDecimal(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to BigDecimal if necessary.
*
* @param column identifying the column
* @return the value
*/
public final BigDecimal getDecimal(ColumnExpr column)
{
return getDecimal(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to boolean if necessary.
*
* @param index index of the column
* @return the value
*/
public boolean getBoolean(int index)
{
return ObjectUtils.getBoolean(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to boolean if necessary.
*
* @param column identifying the column
* @return the value
*/
public final boolean getBoolean(ColumnExpr column)
{
return getBoolean(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to a string if necessary.
*
* @param index index of the column
* @return the value
*/
public String getString(int index)
{
return ObjectUtils.getString(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to a string if necessary.
*
* @param column identifying the column
* @return the value
*/
public final String getString(ColumnExpr column)
{
return getString(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to a Date if necessary.
*
* @param index index of the column
* @return the value
*/
public final Date getDateTime(int index)
{
return ObjectUtils.getDate(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to a Date if necessary.
*
* @param column identifying the column
* @return the value
*/
public final Date getDateTime(ColumnExpr column)
{
return getDateTime(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to a Date if necessary.
*
* @param index index of the column
* @return the value
*/
public final LocalDate getLocalDate(int index)
{
return ObjectUtils.getLocalDate(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to a Date if necessary.
*
* @param column identifying the column
* @return the value
*/
public final LocalDate getLocalDate(ColumnExpr column)
{
return getLocalDate(getFieldIndex(column));
}
/**
* Returns a data value identified by the column index.
* The data value is converted to a Date if necessary.
*
* @param index index of the column
* @return the value
*/
public final LocalDateTime getLocalDateTime(int index)
{
return ObjectUtils.getLocalDateTime(getValue(index));
}
/**
* Returns a data value for the desired column.
* The data value is converted to a Date if necessary.
*
* @param column identifying the column
* @return the value
*/
public final LocalDateTime getLocalDateTime(ColumnExpr column)
{
return getLocalDateTime(getFieldIndex(column));
}
/**
* Returns the value of a field as an enum
* For numeric columns the value is assumed to be an ordinal of the enumeration item
* For non numeric columns the value is assumed to be the name of the enumeration item
*
* @param index index of the field
* @return the enum value
*/
public <T extends Enum<?>> T getEnum(int index, Class<T> enumType)
{ // check for null
if (isNull(index))
return null;
// convert
ColumnExpr col = getColumn(index);
try {
// Convert to enum, depending on DataType
boolean numeric = col.getDataType().isNumeric();
return ObjectUtils.getEnum(enumType, (numeric ? getInt(index) : getValue(index)));
} catch (Exception e) {
// Illegal value
String value = StringUtils.valueOf(getValue(index));
log.error("Unable to resolve enum value of '{}' for type {}", value, enumType.getName());
throw new FieldIllegalValueException(col.getSourceColumn(), value, e);
}
}
/**
* Returns the value of a field as an enum
* For numeric columns the value is assumed to be an ordinal of the enumeration item
* For non numeric columns the value is assumed to be the name of the enumeration item
*
* @param column the column for which to retrieve the value
* @return the enum value
*/
public final <T extends Enum<?>> T getEnum(ColumnExpr column, Class<T> enumType)
{
return getEnum(getFieldIndex(column), enumType);
}
/**
* Returns the value of a field as an enum
* This assumes that the column attribute "enumType" has been set to an enum type
*
* @param column the column for which to retrieve the value
* @return the enum value
*/
@SuppressWarnings("unchecked")
public final <T extends Enum<?>> T getEnum(Column column)
{
Class<Enum<?>> enumType = column.getEnumType();
if (enumType==null)
{ // Not an enum column (Attribute "enumType" has not been set)
throw new InvalidArgumentException("column", column);
}
return getEnum(getFieldIndex(column), (Class<T>)enumType);
}
/**
* Checks whether or not the value for the given column is null.
*
* @param index index of the column
* @return true if the value is null or false otherwise
*/
@Override
public boolean isNull(int index)
{
return ObjectUtils.isEmpty(getValue(index));
}
/**
* Checks whether or not the value for the given column is null.
*
* @param column identifying the column
* @return true if the value is null or false otherwise
*/
@Override
public final boolean isNull(ColumnExpr column)
{
return isNull(getFieldIndex(column));
}
/**
* Returns the value of a column as a formatted text
* This converts the value to a string if necessary and performs an options lookup
* To customize conversion please override convertToString()
* @param column the column for which to get the formatted value
* @return the formatted value
*/
public String getText(ColumnExpr column)
{
String text;
Object value = get(column);
// check options first
Options options = column.getOptions();
if (options!=null && options.has(value))
{ // lookup option
text = options.get(value);
}
else if (value instanceof String)
{ // we already have a string
text = (String)value;
}
else if (ObjectUtils.isEmpty(value))
{ // empty
value = column.getAttribute(Column.COLATTR_NULLTEXT);
text = (value!=null ? value.toString() : StringUtils.EMPTY);
}
else
{ // format value
text = formatValue(column, value);
if (text== null)
text = StringUtils.EMPTY;
}
// done
return text;
}
/**
* Convert a non-string value to a string
* @param column the column expression
* @param value the value to format
* @return the formatted string
*/
protected String formatValue(ColumnExpr column, Object value)
{
return ObjectUtils.getString(value);
}
/**
* Set a single property value of a java bean object used by readProperties.
*/
protected void setBeanProperty(ColumnExpr column, Object bean, String property, Object value)
{
if (StringUtils.isEmpty(property))
property = column.getBeanPropertyName();
try
{
if (bean==null)
throw new InvalidArgumentException("bean", bean);
if (StringUtils.isEmpty(property))
throw new InvalidArgumentException("property", property);
if (log.isTraceEnabled())
log.trace("{}: setting property '{}' to {}", bean.getClass().getName(), property, value);
/*
if (value instanceof Date)
{ // Patch for date bug in BeanUtils
value = DateUtils.addDate((Date)value, 0, 0, 0);
}
*/
// Set Property Value
if (value!=null)
{ // Convert to enum
Class<Enum<?>> enumType = column.getEnumType();
if (enumType!=null)
value = ObjectUtils.getEnum(enumType, value);
// Bean utils will convert if necessary
BeanUtils.setProperty(bean, property, value);
}
else
{ // Don't convert, just set
PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(bean, property);
if (pd==null)
return; // No such property
// get the write method
final Method method = PropertyUtils.getWriteMethod(pd);
if (method == null)
throw new PropertyReadOnlyException(property);
// invoke
method.invoke(bean, value);
}
// IllegalAccessException
} catch (IllegalAccessException e)
{ log.error(bean.getClass().getName() + ": unable to set property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
// InvocationTargetException
} catch (InvocationTargetException e)
{ log.error(bean.getClass().getName() + ": unable to set property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
// NoSuchMethodException
} catch (NoSuchMethodException e)
{ log.error(bean.getClass().getName() + ": unable to set property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
} catch (NullPointerException e)
{ log.error(bean.getClass().getName() + ": unable to set property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
}
}
/**
* Injects the current field values into a java bean.
*
* @return the number of bean properties set on the supplied bean
*/
@Override
public int setBeanProperties(Object bean, Collection<? extends ColumnExpr> ignoreList)
{
// Add all Columns
int count = 0;
for (int i = 0; i < getFieldCount(); i++)
{ // Check Property
ColumnExpr column = getColumn(i);
if (ignoreList != null && ignoreList.contains(column))
continue; // ignore this property
// Get Property Name
String property = column.getBeanPropertyName();
if (property!=null)
setBeanProperty(column, bean, property, this.getValue(i));
count++;
}
return count;
}
/**
* Injects the current field values into a java bean.
*
* @return the number of bean properties set on the supplied bean
*/
public final int setBeanProperties(Object bean)
{
return setBeanProperties(bean, null);
}
}