| /* |
| * 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); |
| } |
| } |
| |
| } |