/* * This software is distributed under the terms of the FSF * Gnu Lesser General Public License (see lgpl.txt). * * This program is distributed WITHOUT ANY WARRANTY. See the * GNU General Public License for more details. */ package com.scooterframework.orm.util; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import com.scooterframework.admin.EnvConfig; import com.scooterframework.common.util.BeanUtil; import com.scooterframework.common.util.Converters; import com.scooterframework.common.util.Message; import com.scooterframework.common.validation.ValidationResults; import com.scooterframework.orm.activerecord.ActiveRecord; import com.scooterframework.orm.activerecord.ReferenceData; import com.scooterframework.orm.activerecord.ReferenceDataStore; import com.scooterframework.orm.sqldataexpress.object.ColumnInfo; import com.scooterframework.orm.sqldataexpress.object.RESTified; import com.scooterframework.orm.sqldataexpress.object.RowData; import com.scooterframework.orm.sqldataexpress.object.RowInfo; import com.scooterframework.orm.sqldataexpress.object.TableData; import com.scooterframework.orm.sqldataexpress.object.TableInfo; /** * DataAccessUtil class has helper methods for objects including ActiveRecord * and SQL data express related instances. * * @author (Fei) John Chen */ public class DataAccessUtil { /** * Gets property value from the object. The object can be of type * <tt>ActiveRecord</tt>, <tt>RowData</tt>, <tt>Properties</tt>, * <tt>Map</tt>, or simply a java bean (POJO). * * <p> * If the object is an ActiveRecord instance, and the property string * consists of dots, this method will treat the dotted string as a path * in association. * </p> * * <pre> * Examples: * //post belongsTo user * getProperty(post, "user.first_name") * => returns first name of the post author * * //lineitem belongsTo order belongsTo customer * getProperty(lineitem, "order.customer.first_name") * => returns first name of the customer who ordered the line item * </pre> * * <p> * It is not recommended to use dotted property string unless you are sure * the object is in a belongs-to or has-one relation chain among all * elements of the dotted property string. * </p> * * @param object * @param property * @return Object */ public static Object getProperty(Object object, String property) { if (object == null) return null; if (property == null) return object; Object data = null; if (object instanceof ActiveRecord) { data = getActiveRecordField((ActiveRecord)object, property); } else if (object instanceof RowData) { data = ((RowData)object).getField(property); } else if (object instanceof Properties) { data = ((Properties)object).getProperty(property); } else if (object instanceof Map) { data = ((Map<?, ?>)object).get(property); } else { data = BeanUtil.getBeanProperty(object, property); } return data; } /** * </p> * Returns field value of a record. If the property string consists of dots, * this method will treat the dotted string as a path in association. * </p> * * <pre> * Examples: * //post belongsTo user * getActiveRecordField(post, "user.first_name") * => returns first name of the post author * * //lineitem belongsTo order belongsTo customer * getActiveRecordField(lineitem, "order.customer.first_name") * => returns first name of the customer who ordered the line item * </pre> * * <p> * It is not recommended to use dotted property string unless you are sure * the object is in a belongs-to or has-one relation chain among all * elements of the dotted property string. * </p> * * @param record * @param property * @return value of the property of the record */ private static Object getActiveRecordField(ActiveRecord record, String property) { if (record == null) return null; if (property.indexOf('.') == -1) return record.getField(property); StringTokenizer st = new StringTokenizer(property, " ."); int total = st.countTokens(); Object tmp = null; int count = 0; ActiveRecord r = record; while(st.hasMoreTokens()) { String token = st.nextToken(); count = count + 1; if (count == total) { tmp = r.getField(token);//The last token is a field name. break; } else { r = r.associated(token).getRecord(); } } return tmp; } /** * Gets all the associated records of an ActiveRecord instance. * * <p> * If the <tt>associationName</tt> string consists of dots, this method * will treat the dotted string as a path in association. * </p> * * <pre> * Examples: * //customer hasMany orders * allAssociatedRecordsOf(customer, "orders") * => returns a list of orders of the customer * * //customer hasMany orders hasMany lineitems * allAssociatedRecordsOf(customer, "orders.lineitems") * => returns a list of lineitems for all orders of the customer * </pre> * * <p> * It is not recommended to use dotted <tt>allAssociatedRecordsOf</tt> string * unless you are sure the object is in a hasMany or hasManyThrough relation * chain among all elements of the dotted string. * </p> * * @param record an ActiveRecord instance. * @param associationName association name. * @return list of associated records. */ public static List<ActiveRecord> allAssociatedRecordsOf(ActiveRecord record, String associationName) { return allAssociatedRecordsOf(record, associationName, false); } /** * Gets all the associated records of an ActiveRecord instance. * * <p>See description of {@link #allAssociatedRecordsOf(com.scooterframework.orm.activerecord.ActiveRecord, * java.lang.String)} method for more details and examples.</p> * * @param record an ActiveRecord instance. * @param associationName association name. * @param refresh true if reload database data * @return list of associated records. */ public static List<ActiveRecord> allAssociatedRecordsOf(ActiveRecord record, String associationName, boolean refresh) { if (record == null || associationName == null) return null; if (associationName.indexOf('.') == -1) return record.allAssociated(associationName, refresh).getRecords(); List<String> associations = Converters.convertStringToList(associationName, "."); int totalAssociations = associations.size(); List<ActiveRecord> records = record.allAssociated((String)associations.get(0), refresh).getRecords(); if (records == null) return null; for (int i = 1; i < totalAssociations; i++) { String associationId = associations.get(i); List<ActiveRecord> tmp = new ArrayList<ActiveRecord>(); int totalRecords = records.size(); for (int j = 0; j < totalRecords; j++) { ActiveRecord r = records.get(j); if (r == null) continue; List<ActiveRecord> rds = r.allAssociated(associationId, refresh).getRecords(); if (rds != null && rds.size() > 0) tmp.addAll(rds); } records = tmp; } return records; } /** * Gets the associated record of an ActiveRecord instance. * * <p> * If the <tt>associationName</tt> string consists of dots, this method * will treat the dotted string as a path in association. * </p> * * <pre> * Examples: * //post belongsTo user * associatedRecordOf(post, "user") * => returns the post author * * //lineitem belongsTo order belongsTo customer * associatedRecordOf(lineitem, "order.customer") * => returns the customer who ordered the line item * </pre> * * <p> * It is not recommended to use dotted <tt>associationName</tt> string * unless you are sure the object is in a belongs-to or has-one relation * chain among all elements of the dotted string. * </p> * * @param record an ActiveRecord instance. * @param associationName association name. * @return the associated record */ public static ActiveRecord associatedRecordOf(ActiveRecord record, String associationName) { return associatedRecordOf(record, associationName, false); } /** * Gets the associated record of an ActiveRecord instance. * * <p>See description of {@link #associatedRecordOf(com.scooterframework.orm.activerecord.ActiveRecord, * java.lang.String)} method for more details and examples.</p> * * @param record an ActiveRecord instance. * @param associationName association name. * @param refresh true if reload database data * @return the associated record */ public static ActiveRecord associatedRecordOf(ActiveRecord record, String associationName, boolean refresh) { if (record == null || associationName == null) return null; if (associationName.indexOf('.') == -1) return record.associated(associationName, refresh).getRecord(); StringTokenizer st = new StringTokenizer(associationName, "."); int total = st.countTokens(); int count = 0; ActiveRecord r = record; while(st.hasMoreTokens()) { String token = st.nextToken(); r = r.associated(token, refresh).getRecord(); count = count + 1; if (count == total) { break; } } return r; } /** * Returns all error messages associated with a record instance. * * @param record an ActiveRecord instance * @return List of error messages. */ public static List<Message> getErrorMessages(ActiveRecord record) { if (record != null) { ValidationResults vr = record.getValidationResults(); if (vr.failed()) { return vr.getErrorMessages(); } } return null; } /** * Gets a list of ReferenceData instances for a certain type. * * @return List */ public static List<ReferenceData> getReferenceDataList(String type) { return ReferenceDataStore.getReferenceDataList(type); } /** * Gets ReferenceData by type and key * * @return ReferenceData */ public static ReferenceData getReferenceDataByTypeAndKey(String type, String keyData) { return ReferenceDataStore.getReferenceDataByTypeAndKey(type, keyData); } /** * Gets ReferenceData by type and value * * @return ReferenceData */ public static ReferenceData getReferenceDataByTypeAndValue(String type, Object valueData) { return ReferenceDataStore.getReferenceDataByTypeAndValue(type, valueData); } /** * Returns home instance of the model. * * @param model model name * @return home instance of the model */ public static ActiveRecord homeInstance(String model) { return EnvConfig.getInstance().getHomeInstance(model); } /** * <p> * Returns an iterator of column names of the model. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param model model name * @return iterator */ public static Iterator<String> columnNames(String model) { return columnNames(homeInstance(model)); } /** * <p> * Returns an iterator of column names of the record. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param record an active record instance or home instance * @return iterator */ public static Iterator<String> columnNames(ActiveRecord record) { return (record != null)?columnNames(record.getRowInfo()):(new ArrayList<String>()).iterator(); } /** * <p> * Returns an iterator of column names. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param rowInfo a RowInfo instance * @return iterator */ public static Iterator<String> columnNames(RowInfo rowInfo) { Iterator<String> it = null; if (rowInfo != null) { String[] columnNames = rowInfo.getColumnNames(); it = Converters.convertArrayToList(columnNames).iterator(); } else { it = (new ArrayList<String>()).iterator(); } return it; } /** * <p> * Returns an iterator of column names. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param td a TableData instance * @return iterator */ public static Iterator<String> columnNames(TableData td) { return (td != null)?columnNames(td.getHeader()):(new ArrayList<String>()).iterator(); } /** * <p> * Returns an iterator of column names. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param rd a RowData instance * @return iterator */ public static Iterator<String> columnNames(RowData rd) { return (rd != null)?columnNames(rd.getRowInfo()):(new ArrayList<String>()).iterator(); } /** * <p> * Returns an iterator of column names. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param record a restified record * @return iterator */ public static Iterator<String> columnNames(RESTified record) { if (record != null) { if (record instanceof ActiveRecord) return columnNames((ActiveRecord)record); if (record instanceof RowData) return columnNames((RowData)record); } return (new ArrayList<String>()).iterator(); } /** * <p> * Returns an iterator of column names of the record in the <tt>records</tt> * collection. This is a safe method. If input record is null, an empty * iterator is still returned. * </p> * * @param records a collection of records * @return iterator */ public static Iterator<String> columnNames(Collection<?> records) { Object record = null; if (records != null) { Iterator<?> it = records.iterator(); if (it.hasNext()) record = it.next(); } if (record instanceof ActiveRecord) return columnNames((ActiveRecord)record); if (record instanceof RowData) return columnNames((RowData)record); return (new ArrayList<String>()).iterator(); } /** * <p> * Returns an iterator of ColumnInfo instances of the model. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param model model name * @return iterator */ public static Iterator<ColumnInfo> columns(String model) { return columns(homeInstance(model)); } /** * <p> * Returns an iterator of ColumnInfo instances of the record. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param record an ActiveRecord record instance or home instance * @return an iterator of ColumnInfo instances */ public static Iterator<ColumnInfo> columns(ActiveRecord record) { return (record != null)?columns(record.getRowInfo()):(new ArrayList<ColumnInfo>()).iterator(); } /** * <p> * Returns an iterator of ColumnInfo instances. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param rowInfo a RowInfo instance * @return iterator */ public static Iterator<ColumnInfo> columns(RowInfo rowInfo) { Iterator<ColumnInfo> it = null; if (rowInfo != null) { it = rowInfo.columns().iterator(); } if (it == null) { it = (new ArrayList<ColumnInfo>()).iterator(); } return it; } /** * <p> * Returns an iterator of ColumnInfo instances of the record. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param record a restified record * @return an iterator of ColumnInfo instances */ public static Iterator<ColumnInfo> columns(RESTified record) { if (record != null) { if (record instanceof ActiveRecord) return columns((ActiveRecord)record); if (record instanceof RowData) return columns((RowData)record); } return (new ArrayList<ColumnInfo>()).iterator(); } /** * <p> * Returns an iterator of ColumnInfo instances of the record. This is a safe * method. If input record is null, an empty iterator is still returned. * </p> * * @param rd a RowData record instance * @return an iterator of ColumnInfo instances */ public static Iterator<ColumnInfo> columns(RowData rd) { return (rd != null)?columns(rd.getRowInfo()):(new ArrayList<ColumnInfo>()).iterator(); } /** * <p> * Returns an iterator of ColumnInfo instances of the <tt>records</tt> * collection. This is a safe method. If input record is null, an empty * iterator is still returned. * </p> * * @param records a collection of records * @return an iterator of ColumnInfo instances */ public static Iterator<ColumnInfo> columns(Collection<?> records) { Object record = null; if (records != null) { Iterator<?> it = records.iterator(); if (it.hasNext()) record = it.next(); } if (record instanceof ActiveRecord) return columns((ActiveRecord)record); if (record instanceof RowData) return columns((RowData)record); return (new ArrayList<ColumnInfo>()).iterator(); } /** * <p> * Returns an iterator of a collection. This is a safe method. If the * collection is null, an empty iterator is still returned. * </p> * * @param items a collection of items * @return an iterator of the collection */ public static <T> Iterator<T> iteratorOf(Collection<T> items) { return (items != null)?items.iterator():(new ArrayList<T>()).iterator(); } /** * <p> * Returns an iterator of a map. This is a safe method. If the * map is null, an empty iterator is still returned. * </p> * * @param map a map * @return an iterator of the collection */ public static <K, V> Iterator<K> iteratorOf(Map<K, V> map) { return (map != null)?map.keySet().iterator():(new ArrayList<K>()).iterator(); } /** * <p> * Returns a RowInfo instance of the model. This is a safe * method. If input model is null, an empty RowInfo instance is still returned. * </p> * * @param model model name * @return RowInfo for the model */ public static RowInfo getRowInfo(String model) { RowInfo ri = homeInstance(model).getRowInfo(); return (ri != null)?ri:(new RowInfo()); } /** * Returns restful id of the record. If it is null, return empty string. * * @param record a RESTified record * @return restful id of the record. */ public static String restfulIdOf(RESTified record) { return (record != null)?record.getRestfulId():""; } /** * Returns RowInfo attribute of the record. * * @param record a record object * @return RowInfo attribute of the record. */ public static RowInfo rowInfoOf(ActiveRecord record) { return (record != null)?record.getRowInfo():(new RowInfo()); } /** * Returns RowInfo attribute of the record. * * @param record a record object * @return RowInfo attribute of the record. */ public static RowInfo rowInfoOf(RowData record) { return (record != null)?record.getRowInfo():(new RowInfo()); } /** * Returns RowInfo attribute of the TableData instance. * * @param tableData a TableData object * @return RowInfo attribute of the record. */ public static RowInfo rowInfoOf(TableData tableData) { return (tableData != null)?tableData.getHeader():(new RowInfo()); } /** * Returns RowInfo attribute of the TableInfo instance. * * @param tableInfo a TableInfo object * @return RowInfo attribute of the record. */ public static RowInfo rowInfoOf(TableInfo tableInfo) { return (tableInfo != null)?tableInfo.getHeader():(new RowInfo()); } }