blob: f481f68c3052b5fdeb29f45d8ac20639d68d56d6 [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.data.bean;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.empire.commons.ObjectUtils;
import org.apache.empire.commons.Options;
import org.apache.empire.data.Column;
import org.apache.empire.data.ColumnExpr;
import org.apache.empire.data.Record;
import org.apache.empire.exceptions.BeanPropertyGetException;
import org.apache.empire.exceptions.BeanPropertySetException;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.ItemNotFoundException;
import org.apache.empire.exceptions.ObjectNotValidException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* BeanRecordProxy
* This class defines proxy that allows any POJO to behave like a record object.
*
* @param <T> the type of the class proxied by this {@code BeanRecordProxy}
*
* @author Rainer
*/
public class BeanRecordProxy<T> implements Record
{
protected static final Logger log = LoggerFactory.getLogger(BeanRecordProxy.class);
protected List<Column> columns;
protected Column[] keyColumns;
protected boolean[] modified;
protected T data;
public BeanRecordProxy(T data, List<Column> columns, Column[] keyColumns)
{
this.data = data;
this.columns = columns;
this.keyColumns = keyColumns;
}
public BeanRecordProxy(List<Column> columns, Column[] keyColumns)
{
this(null, columns, keyColumns);
}
public BeanRecordProxy(T data, BeanClass beanClass)
{
this(data,
ObjectUtils.convert(Column.class, beanClass.getProperties()),
beanClass.getKeyColumns());
}
public BeanRecordProxy(BeanClass beanClass)
{
this(null,
ObjectUtils.convert(Column.class, beanClass.getProperties()),
beanClass.getKeyColumns());
}
public T getBean()
{
return data;
}
public void setBean(T data)
{
this.data = data;
}
@Override
public Column getColumn(int index)
{
return columns.get(index);
}
@Override
public ColumnExpr getColumnExpr(int index)
{
return columns.get(index);
}
@Override
public Column[] getKeyColumns()
{
return keyColumns;
}
/**
* Returns the array of primary key columns.
* @return the array of primary key columns
*/
public Object[] getKeyValues()
{
if (keyColumns==null)
return null;
// Get key values
Object[] key = new Object[keyColumns.length];
for (int i=0; i<keyColumns.length; i++)
key[i] = this.getValue(keyColumns[i]);
// the key
return key;
}
@Override
public int getFieldCount()
{
return columns.size();
}
@Override
public int getFieldIndex(ColumnExpr column)
{
for (int i=0; i<columns.size(); i++)
{
if (columns.get(i).equals(column))
return i;
}
return -1;
}
@Override
public int getFieldIndex(String columnName)
{
for (int i=0; i<columns.size(); i++)
{
if (columns.get(i).getName().equals(columnName))
return i;
}
return -1;
}
@Override
public Options getFieldOptions(Column column)
{
return column.getOptions();
}
@Override
public boolean isFieldVisible(Column column)
{
return true;
}
@Override
public boolean isFieldReadOnly(Column column)
{
if (isNew()==false && ObjectUtils.contains(keyColumns, column))
return true;
if (column.isAutoGenerated())
return true;
return column.isReadOnly();
}
@Override
public boolean isFieldRequired(Column column)
{
return column.isRequired();
}
@Override
public boolean isModified()
{
return (modified!=null);
}
@Override
public boolean isNew()
{
if (!isValid())
throw new ObjectNotValidException(this);
// Record is new until all key fields have been supplied
if (keyColumns!=null)
{ // Check all Key Columns
for (int i=0; i<keyColumns.length; i++)
{
Object value = getValue(keyColumns[i]);
if ((value instanceof Number) && ((Number)value).longValue()==0)
return true;
if (ObjectUtils.isEmpty(value))
return true;
}
}
// Not new
return false;
}
@Override
public boolean isValid()
{
return (data!=null);
}
@Override
public boolean isReadOnly()
{
return (isValid() ? false : true);
}
@Override
public Object getValue(ColumnExpr column)
{
if (!isValid())
throw new ObjectNotValidException(this);
// getBeanPropertyValue
return getBeanPropertyValue(data, column);
}
@Override
public Object getValue(int index)
{
return getValue(getColumn(index));
}
@Override
public boolean isNull(ColumnExpr column)
{
return ObjectUtils.isEmpty(getValue(column));
}
@Override
public boolean isNull(int index)
{
return isNull(getColumn(index));
}
/**
* Validates a value before it is set in the record.
*/
@Override
public Object validateValue(Column column, Object value)
{
return column.validate(value);
}
/**
* sets the value of a field.
*/
@Override
public void setValue(Column column, Object value)
{
if (!isValid())
throw new ObjectNotValidException(this);
// Track modification status
if (ObjectUtils.compareEqual(getValue(column), value)==false)
{
if (modified== null)
modified = new boolean[columns.size()];
modified[getFieldIndex(column)] = true;
}
// validate
value = validateValue(column, value);
// Set Value
setBeanPropertyValue(data, column, value);
}
/**
* sets the value of a field.
*/
@Override
public final void setValue(int i, Object value)
{
setValue(getColumn(i), value);
}
/**
* Detects whether or not a particular field has been modified.
*/
@Override
public boolean wasModified(Column column)
{
int index = getFieldIndex(column);
if (index<0)
throw new ItemNotFoundException(column.getName());
// check modified
return (modified!=null && modified[index]);
}
/**
* clears the modification status of the object and all fields.
*/
public void clearModified()
{
modified = null;
}
// --------------- Bean support ------------------
@Override
public int setBeanProperties(Object bean)
{
return setBeanProperties(bean, null);
}
@Override
public int setBeanProperties(Object bean, Collection<ColumnExpr> ignoreList)
{
// Add all Columns
int count = 0;
for (int i = 0; i < getFieldCount(); i++)
{ // Check Property
Column column = getColumn(i);
if (column.isReadOnly())
continue;
if (ignoreList != null && ignoreList.contains(column))
continue; // ignore this property
// Get Property Name
setBeanPropertyValue(bean, column, getValue(i));
}
return count;
}
@Override
public int setRecordValues(Object bean, Collection<Column> ignoreList)
{
// Add all Columns
int count = 0;
for (int i = 0; i < getFieldCount(); i++)
{ // Check Property
Column column = getColumn(i);
if (column.isReadOnly())
continue;
if (ignoreList != null && ignoreList.contains(column))
continue; // ignore this property
// Get Property Name
String property = column.getBeanPropertyName();
Object value = getBeanPropertyValue(bean, property);
setValue(column, value);
count++;
}
return count;
}
@Override
public int setRecordValues(Object bean)
{
return setRecordValues(bean, null);
}
// --------------- protected ------------------
protected Object getBeanPropertyValue(Object bean, ColumnExpr column)
{
// Check Params
if (bean==null)
throw new InvalidArgumentException("bean", bean);
if (column==null)
throw new InvalidArgumentException("column", column);
// getBeanPropertyValue
return getBeanPropertyValue(bean, column.getBeanPropertyName());
}
protected Object getBeanPropertyValue(Object bean, String property)
{
// Check Params
if (bean==null)
throw new InvalidArgumentException("bean", bean);
if (property==null)
throw new InvalidArgumentException("property", property);
try
{ // Get Property Value
PropertyUtilsBean pub = BeanUtilsBean.getInstance().getPropertyUtils();
return pub.getSimpleProperty(bean, property);
} catch (IllegalAccessException e)
{ log.error(bean.getClass().getName() + ": unable to get property '" + property + "'");
throw new BeanPropertyGetException(bean, property, e);
} catch (InvocationTargetException e)
{ log.error(bean.getClass().getName() + ": unable to get property '" + property + "'");
throw new BeanPropertyGetException(bean, property, e);
} catch (NoSuchMethodException e)
{ log.warn(bean.getClass().getName() + ": no getter available for property '" + property + "'");
throw new BeanPropertyGetException(bean, property, e);
}
}
protected void setBeanPropertyValue(Object bean, Column column, Object value)
{
// Check Params
if (bean==null)
throw new InvalidArgumentException("bean", bean);
if (column==null)
throw new InvalidArgumentException("column", column);
// Get Property Name
String property = column.getBeanPropertyName();
try
{ // Get Property Value
if (ObjectUtils.isEmpty(value))
value = null;
// Set Property Value
if (value!=null)
{ // Bean utils will convert if necessary
BeanUtils.setProperty(bean, property, value);
}
else
{ // Don't convert, just set
PropertyUtils.setProperty(bean, property, null);
}
} catch (IllegalArgumentException e) {
log.error(bean.getClass().getName() + ": invalid argument for property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
} catch (IllegalAccessException e)
{ log.error(bean.getClass().getName() + ": unable to set property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
} catch (InvocationTargetException e)
{ log.error(bean.getClass().getName() + ": unable to set property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
} catch (NoSuchMethodException e) {
log.error(bean.getClass().getName() + ": no setter available for property '" + property + "'");
throw new BeanPropertySetException(bean, property, e);
}
}
}