Java tutorial
/* * NorthRidge Software, LLC - Copyright (c) 2019. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.nridge.core.base.field.data; import com.nridge.core.base.field.CellValue; import com.nridge.core.base.field.Field; import com.nridge.core.base.field.FieldRow; import com.nridge.core.base.std.DigitalHash; import com.nridge.core.base.std.StrUtl; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A DataTable manages a row x column matrix of <i>DataFields</i>. Use this * class when you need to model meta data fields. In addition, you can specify * also specify a sort order for data sources to utilize during fetch operations. * <p> * This framework provides a number of helper classes that accept a DataTable * for IO operations. * </p> * * @author Al Cole * @since 1.0 */ public class DataTable { private int mColumnCount; private FieldRow mNewRow; private DataBag mColumns; private String[] mOffsetNameMap; private ArrayList<FieldRow> mRows; private String mName = StringUtils.EMPTY; private HashMap<String, String> mFeatures; private String mSortFieldName = StringUtils.EMPTY; private transient HashMap<String, Object> mProperties; /** * Default constructor. */ public DataTable() { mColumns = new DataBag(); mRows = new ArrayList<FieldRow>(); mFeatures = new HashMap<String, String>(); } /** * Constructor accepts a table name parameter and initializes the DataTable. * * @param aName Name of table. */ public DataTable(String aName) { setName(aName); mColumns = new DataBag(); mRows = new ArrayList<FieldRow>(); mFeatures = new HashMap<String, String>(); } /** * Constructor accepts a {@link DataBag} as an initialization parameter. * The bag will be used to assign the column fields for the table. * * @param aBag Persist bag. */ public DataTable(final DataBag aBag) { mRows = new ArrayList<FieldRow>(); mFeatures = new HashMap<String, String>(); setColumns(new DataBag(aBag)); String bagName = aBag.getName(); if (StringUtils.isNotEmpty(bagName)) setName(bagName); } /** * Constructor clones an existing DataTable (name, title, rows, columns). * * @param aTable Source table instance to clone. * @param aStartOffset Starting offset into table. * @param aLimit Maximum number of rows (0 implies all) */ public DataTable(final DataTable aTable, int aStartOffset, int aLimit) { if (aTable != null) { this.mRows = new ArrayList<FieldRow>(); this.setName(aTable.getName()); this.setSortFieldName(aTable.getSortFieldName()); this.mFeatures = new HashMap<String, String>(aTable.getFeatures()); this.setColumns(new DataBag(aTable.getColumnBag())); int rowCount = aTable.rowCount(); if (rowCount > 0) { FieldRow fieldRow; if (aStartOffset < 0) aStartOffset = 0; if (aLimit >= rowCount) aLimit = rowCount; int recordCount = 0; for (int row = aStartOffset; (row < rowCount) && (recordCount < aLimit); row++) { recordCount++; fieldRow = aTable.getRow(row); this.addRow(new FieldRow(fieldRow)); } } } } /** * Constructor clones an existing DataTable (name, title, rows, columns). * * @param aTable Source table instance to clone. */ public DataTable(final DataTable aTable) { if (aTable != null) { this.mRows = new ArrayList<FieldRow>(); this.setName(aTable.getName()); this.setSortFieldName(aTable.getSortFieldName()); this.mFeatures = new HashMap<String, String>(aTable.getFeatures()); this.setColumns(new DataBag(aTable.getColumnBag())); for (FieldRow fieldRow : aTable.getRows()) this.addRow(new FieldRow(fieldRow)); } } /** * Creates a new table by extending the current table with columns in the * parameter bag followed by adding the bag as a new row. * * @param aBag Bag of new column fields to add to the table. * @param aRowOffset Row offset to insert the bag at. * * @return DataTable A table reflecting the new columns. */ public DataTable addNewColumnsAndRow(DataBag aBag, int aRowOffset) { DataBag rowBag; DataTable newTable; DataField curDataField, newDataField; if (StringUtils.isEmpty(mName)) { newTable = new DataTable(mColumns); newTable.setName(aBag.getName()); } else { newTable = new DataTable(mColumns); newTable.setName(mName); } newTable.setFeatures(getFeatures()); newTable.setSortFieldName(getSortFieldName()); for (DataField ncbDataField : aBag.getFields()) { curDataField = mColumns.getFieldByName(ncbDataField.getName()); if (curDataField == null) { newDataField = new DataField(ncbDataField); newDataField.clearValues(); newTable.add(newDataField); } else newTable.add(curDataField); } int curRowCount = rowCount(); for (int row = 0; row < curRowCount; row++) { rowBag = getRowAsBag(row); if (row == aRowOffset) newTable.addRow(aBag); newTable.addRow(rowBag); } return newTable; } /** * Creates a new instance of the data table based on the * one currently populated. The new instance will contain * only those columns that have been populated from previous * assignments. This can be helpful when you start with a * schema table containing many possible columns that never * end up getting populated in a load operation. * * @return Data table instance. */ public DataTable collapseUnusedColumns() { FieldRow newRow; DataTable newTable; String newFieldName; DataField curDataField, newDataField; if (StringUtils.isEmpty(mName)) newTable = new DataTable(); else newTable = new DataTable(mName); newTable.setFeatures(getFeatures()); newTable.setSortFieldName(getSortFieldName()); DataBag newBag = mColumns.collapseUnusedFields(); newTable.setColumns(newBag); int curRowCount = rowCount(); int newColCount = newBag.count(); for (int row = 0; row < curRowCount; row++) { newRow = null; for (int col = 0; col < newColCount; col++) { newDataField = newBag.getByOffset(col); newFieldName = newDataField.getName(); curDataField = getFieldByRowName(row, newFieldName); if (curDataField != null) { if (newRow == null) newRow = newTable.newRow(); if (curDataField.isMultiValue()) newTable.setValuesByName(newFieldName, curDataField.getValues()); else newTable.setValueByName(newFieldName, curDataField.getValue()); } } if (newRow != null) newTable.addRow(); } return newTable; } /** * Returns the name of the table. * * @return Table name. */ public String getName() { return mName; } /** * Assigns the name of the table. * * @param aName Table name. */ public void setName(String aName) { mName = aName; if ((mColumns != null) && (StringUtils.isEmpty(mColumns.getName()))) mColumns.setName(aName); } /** * Returns a string summary representation of a DataTable. * * @return String summary representation of this DataTable. */ @Override public String toString() { String idName; int rowCount, colCount; if (StringUtils.isEmpty(mName)) idName = "Data Table"; else idName = mName; if (mRows == null) rowCount = 0; else rowCount = mRows.size(); if (mColumns == null) colCount = 0; else colCount = mColumns.count(); return String.format("%s [%d cols x %d rows]", idName, colCount, rowCount); } /** * You can designate one field (column) for sorting in a table. This * method returns the name of that field or an empty string. * * @return Name of the sort field or an empty string if unassigned. */ public String getSortFieldName() { return mSortFieldName; } /** * You can designate one field (column) for sorting in a table. This * method assigns that field name. * * @param aName Field name. */ public void setSortFieldName(String aName) { mSortFieldName = aName; } /** * Assigns the fields contained within the bag to the columns of * the table. * <p> * <b>Note:</b> This method will only update the columns if the table * is empty (e.g. no rows have been added to the table). * </p> * * @param aBag Data bag of fields. */ public void setColumns(DataBag aBag) { if ((aBag != null) && ((mRows == null) || (mRows.size() == 0))) { mColumns = aBag; mColumnCount = mColumns.count(); } } /** * Returns the count of column fields in this table. * * @return Count of column fields. */ public int columnCount() { mColumnCount = mColumns.count(); return mColumnCount; } /** * Returns the collection of field rows to derived classes. * * @return A list of internal field rows. */ public ArrayList<FieldRow> getRows() { return mRows; } /** * Returns a list of bags based on the rows within the table. * Please note that each row is copied into a DataBag - any * changes to the the fields in the bag will not find their * way back to the original table row. * * @return A list of data bag instances. */ public ArrayList<DataBag> getAsBagList() { ArrayList<DataBag> dataBagList = new ArrayList<DataBag>(); for (FieldRow fieldRow : mRows) dataBagList.add(getRowAsBag(fieldRow)); return dataBagList; } private void populateOffsetNameMap() { if (mOffsetNameMap == null) { int offset = 0; mColumnCount = mColumns.count(); mOffsetNameMap = new String[mColumnCount]; ArrayList<DataField> bagFields = mColumns.getFields(); for (DataField dataField : bagFields) mOffsetNameMap[offset++] = dataField.getName(); } } /** * Assigns the list of {@link FieldRow}s to this table. This * method is typically used by IO helper classes to reconstruct * the contents of a table. * * @param aRows List of field rows. */ public void setRows(ArrayList<FieldRow> aRows) { if ((aRows != null) && (aRows.size() > 0)) { int rowCount = aRows.size(); FieldRow fieldRow = aRows.get(0); if (fieldRow.count() == mColumns.count()) { populateOffsetNameMap(); mRows = new ArrayList<FieldRow>(rowCount); for (int row = 0; row < rowCount; row++) addRow(new FieldRow(aRows.get(row))); } } else mRows = new ArrayList<FieldRow>(); } /** * Adds the {@link DataField} to collection of columns in the * table. * <p> * <b>Note:</b> This method will only add the column if the table is * empty (e.g. no rows have been added to the table). * </p> * * @param aField A field to add as a column. */ public void add(DataField aField) { if (mRows.size() == 0) mColumns.add(aField); } /** * Returns the <i>FieldRow</i> identified by the row offset parameter * value. * * @param aRowOffset Row offset into table. * * @return Field row representation of the column data. */ public FieldRow getRow(int aRowOffset) { if (aRowOffset < mRows.size()) return mRows.get(aRowOffset); else return null; } /** * Returns the <i>DataField</i> identified by the column offset * parameter. * * @param aColOffset Column offset. * * @return Simple field representation of the column definition. */ public DataField getColumn(int aColOffset) { return mColumns.getByOffset(aColOffset); } /** * Returns the <i>DataBag</i> representation of the field columns * defined for the table. * * @return Data bag representation of the table columns. */ public DataBag getColumnBag() { return mColumns; } /** * Convenience method that transforms the cells of a field row into * a <i>DataBag</i> representation. * * @param aFieldRow Field row. * * @return Data bag instance. */ public DataBag getRowAsBag(FieldRow aFieldRow) { DataField dataField; DataBag dataBag = new DataBag(mColumns); ArrayList<DataField> bagFields = dataBag.getFields(); for (int col = 0; col < mColumnCount; col++) { dataField = bagFields.get(col); if (dataField.isMultiValue()) dataField.setValues(aFieldRow.getValues(col)); else dataField.setValue(aFieldRow.getValue(col)); } return dataBag; } /** * Convenience method that transforms the field row identified * by the row offset parameter into a <i>DataBag</i>. * * @param aRowOffset Row offset into table. * * @return Data bag. */ public DataBag getRowAsBag(int aRowOffset) { return getRowAsBag(getRow(aRowOffset)); } /** * Returns a new <i>FieldRow</i> suitable for updating and later * adding to the table. * <p> * <b>Note:</b> As a convenience, the class will maintain an * internal reference to the last field row created via this * method. Several setValue() methods will recognize this * internal field row reference when assigning values to * cells in a row. * </p> * * @return Field row initialized with column cells. * * @see <code>DataTable.setValueByName()</code> */ public FieldRow newRow() { populateOffsetNameMap(); mNewRow = new FieldRow(mColumnCount); return mNewRow; } /** * Convenience method that adds the <i>DataBag</i> as a new row * in the table. * * @param aBag Bag of fields. */ public void addRow(DataBag aBag) { if (aBag != null) { FieldRow fieldRow = newRow(); ArrayList<DataField> bagFields = aBag.getFields(); for (DataField dataField : bagFields) setValueByName(fieldRow, dataField.getName(), dataField.getValue()); addRow(fieldRow); } } /** * Adds the <i>FieldRow</i> parameter to the table. * * @param aRow Field row. * * @see <code>DataTable.newRow()</code> */ public void addRow(FieldRow aRow) { CellValue cellValue; for (int col = 0; col < mColumnCount; col++) { cellValue = aRow.getCellValue(col); if (cellValue.isAssigned()) markColumnAssigned(col); } mRows.add(aRow); } /** * Adds the internally managed field row reference to the table. The * parent application should not use this method unless it called * <code>DataTable.newRow()</code> previously and populated the cell * values. */ public void addRow() { if (mNewRow != null) { mRows.add(mNewRow); mNewRow = null; } } /** * Removes the row identified by the offset parameter from the table. * * @param aRowOffset Row offset. */ public void removeRow(int aRowOffset) { if (aRowOffset < rowCount()) mRows.remove(aRowOffset); } /** * Removes the field row identified by the <i>FieldRow</i> parameter * from the table. * * @param aRow Field row. */ public void removeRow(FieldRow aRow) { if (aRow != null) { int rowOffset = 0; for (FieldRow fieldRow : mRows) { if (fieldRow.isEqual(aRow)) { mRows.remove(rowOffset); break; } else rowOffset++; } } } /** * Empties the table of any field rows. The columns and other * properties remain unchanged. */ public void emptyRows() { mRows = new ArrayList<FieldRow>(); } /** * Empties the table of any field rows and columns. */ public void empty() { mFeatures.clear(); mColumns = new DataBag(); mRows = new ArrayList<FieldRow>(); } /** * Returns the number of field rows in the table. * * @return Total rows. */ public int rowCount() { return mRows.size(); } /** * Convenience method that identifies the offset of the column that * has a field name matching the parameter name. * * @param aName Field name of column. * * @return Offset of field column or -1 if not found. */ private int offsetByName(String aName) { if (StringUtils.isNotEmpty(aName)) { populateOffsetNameMap(); for (int offset = 0; offset < mColumnCount; offset++) { if (mOffsetNameMap[offset].equals(aName)) return offset; } } return -1; } private void markColumnAssigned(int aCol) { if ((aCol >= 0) && (aCol < mColumnCount)) { DataField colField = mColumns.getByOffset(aCol); if ((colField != null) && (!colField.isAssigned())) colField.setAssignedFlag(true); } } /** * Assigns a value to the cell located at row and column. * * @param aRow Offset into the rows of the table. * @param aCol Offset into the columns of the table. * @param aValue Value to assign. * */ public void setValueByRowColumn(int aRow, int aCol, String aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) { markColumnAssigned(aCol); fieldRow.setValue(aCol, aValue); } } /** * Assigns multiple values to the cell located at row and column. * * @param aRow Offset into the rows of the table. * @param aCol Offset into the columns of the table. * @param aValues Values to assign. * */ public void setValuesByRowColumn(int aRow, int aCol, ArrayList<String> aValues) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) { markColumnAssigned(aCol); fieldRow.setValues(aCol, aValues); } } /** * Assigns the value parameter to the cell of the internally * managed row identified by the column. * * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aCol Offset into the columns of the table. * @param aValue Cell value to assign. */ public void setValueByColumn(int aCol, String aValue) { if (mNewRow != null) { markColumnAssigned(aCol); mNewRow.setValue(aCol, aValue); } } /** * Assigns the values parameter to the cell of the internally * managed row identified by the column. * * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aCol Offset into the columns of the table. * @param aValues Cell values to assign. */ public void setValuesByColumn(int aCol, ArrayList<String> aValues) { if (mNewRow != null) { markColumnAssigned(aCol); mNewRow.setValues(aCol, aValues); } } /** * Returns a newly created data field from the cell at location * row, column. * * <p> * <b>Note:</b> The field is a copy of the original table cell. * Any modifications to the field will NOT be reflected in the * table. * </p> * * @param aRow Offset into the rows of the table. * @param aCol Offset into the columns of the table. * * @return Data field if successfully located or <i>null</i>. */ public DataField getFieldByRowCol(int aRow, int aCol) { DataField dataField = null; FieldRow fieldRow = getRow(aRow); if (fieldRow != null) { dataField = new DataField(mColumns.getByOffset(aCol)); dataField.setValues(fieldRow.getValues(aCol)); } return dataField; } /** * Returns the field located at the row and matches the field * name parameter. * * <p> * <b>Note:</b> The field is a copy of the original table cell. * Any modifications to the field will NOT be reflected in the * table. * </p> * * @param aRow Offset into the rows of the table. * @param aName Name of the field to match in the row. * * @return Data field if successfully located or <i>null</i>. */ public DataField getFieldByRowName(int aRow, String aName) { DataField dataField = null; FieldRow fieldRow = getRow(aRow); if (fieldRow != null) { int colOffset = offsetByName(aName); dataField = new DataField(mColumns.getByOffset(colOffset)); dataField.setValues(fieldRow.getValues(colOffset)); } return dataField; } /** * Returns a {@link DataField} representing the cell identified * by the column offset parameter. * * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. Also, the field is a copy of the * original table cell. Any modifications to the field will NOT * be reflected in the table. * </p> * * @param aCol Column offset. * @return Data field or <i>null</i> if not found. */ public DataField getFieldByColumn(int aCol) { DataField dataField; dataField = new DataField(mColumns.getByOffset(aCol)); if ((dataField != null) && (mNewRow != null)) dataField.setValues(mNewRow.getValues(aCol)); return dataField; } /** * Returns the cell value for the column name contained * within the field row parameter. * * @param aRow Field row. * @param aName Name of the column. * * @return Cell value or an empty string if not found. * * @see <code>DataTable.getRow()</code> */ public String getValueByName(FieldRow aRow, String aName) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); return aRow.getValue(colOffset); } /** * Returns the cell values for the column name contained * within the field row parameter. * * @param aRow Field row. * @param aName Name of the column. * * @return Cell values. * * @see <code>DataTable.getRow()</code> */ public ArrayList<String> getValuesByName(FieldRow aRow, String aName) { return aRow.getValues(offsetByName(aName)); } /** * Returns the value of the column name for the row contained within * the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * * @return Cell value or an empty string if not found. * * @see <code>DataTable.rowCount()</code> */ public String getValueByName(int aRow, String aName) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) return getValueByName(fieldRow, aName); else return StringUtils.EMPTY; } /** * Returns the values of the column name for the row contained within * the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * * @return Cell values. * * @see <code>DataTable.rowCount()</code> */ public ArrayList<String> getValuesByName(int aRow, String aName) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) return getValuesByName(fieldRow, aName); else return new ArrayList<String>(); } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, String aValue) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValue(colOffset, aValue); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, String aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value list to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValues Value list to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValuesByName(int aRow, String aName, ArrayList<String> aValues) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValuesByName(fieldRow, aName, aValues); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, String aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Assigns the list of values to the cell identified by the column * name of the internally managed field row. * <p> * <b>Note:</b> The parent application must call<code>DataTable.addRow()</code> * prior to using this method. * </p> * * @param aName Name of the column. * @param aValues Cell values to assign. * * @see <code>DataTable.addRow()</code> */ public void setValuesByName(String aName, ArrayList<String> aValues) { if (mNewRow != null) setValuesByName(mNewRow, aName, aValues); } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, int aValue) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValue(colOffset, ((Integer) aValue).toString()); } /** * Assigns the list of values to the cell identified by the column * name contained within the field row.. * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aRow Field row. * @param aName Name of the column. * @param aValues Cell values to assign. * * @see <code>DataTable.addRow()</code> */ public void setValuesByName(FieldRow aRow, String aName, ArrayList<String> aValues) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValues(colOffset, aValues); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, int aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, int aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, long aValue) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValue(colOffset, ((Long) aValue).toString()); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, long aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, long aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, float aValue) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValue(colOffset, ((Float) aValue).toString()); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, float aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call * <code>DataTable.addRow()</code> prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, float aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, double aValue) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValue(colOffset, ((Double) aValue).toString()); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, double aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call {@link DataTable}.addRow() * prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, double aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, boolean aValue) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); aRow.setValue(colOffset, StrUtl.booleanToString(aValue)); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, boolean aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call * <code>DataTable.addRow()</code> prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, boolean aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name * contained within the field row. Since the value parameter * represents a <i>Date</i>, this method accepts a format mask * parameter. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @param aFormatMask Format mask string. Refer to <i>Field</i> for examples. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, Date aValue, String aFormatMask) { if (aValue != null) { int colOffset = offsetByName(aName); markColumnAssigned(colOffset); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(aFormatMask); aRow.setValue(colOffset, simpleDateFormat.format(aValue.getTime())); } } /** * Assigns the value to the cell identified by the column name * contained within the field row. * * @param aRow Field row. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.getRow()</code> */ public void setValueByName(FieldRow aRow, String aName, Date aValue) { setValueByName(aRow, aName, aValue, Field.FORMAT_DATETIME_DEFAULT); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, Date aValue) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. Since the value parameter * represents a <i>Date</i>, this method accepts a format mask * parameter. * <p> * <b>Note:</b> The parent application must call * <code>DataTable.addRow()</code> prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * @param aFormatMask Format mask string. Refer to {@link Field} for examples. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, Date aValue, String aFormatMask) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue, aFormatMask); } /** * Assigns the value to the cell identified by the row offset and * column name within the table. Since the value parameter * represents a <i>Date</i>, this method accepts a format mask * parameter. * * @param aRow Row offset into the table. * @param aName Name of the column. * @param aValue Value to assign to the cell. * * @param aFormatMask Format mask string. Refer to <i>Field</i> for examples. * * @see <code>DataTable.rowCount()</code> */ public void setValueByName(int aRow, String aName, Date aValue, String aFormatMask) { FieldRow fieldRow = getRow(aRow); if (fieldRow != null) setValueByName(fieldRow, aName, aValue, aFormatMask); } /** * Assigns the value to the cell identified by the column name of * the internally managed field row. * <p> * <b>Note:</b> The parent application must call * <code>DataTable.addRow()</code> prior to using this method. * </p> * * @param aName Name of the column. * @param aValue Cell value to assign. * * @see <code>DataTable.addRow()</code> */ public void setValueByName(String aName, Date aValue) { if (mNewRow != null) setValueByName(mNewRow, aName, aValue); } /** * Returns the cell value contained within the array of field rows at the * specified row offset and matching the column name. * * @param aRows Array list of field rows. * @param aRowOffset Row offset into the array list. * @param aName Name of the column. * * @return Cell value or an empty string if not found. */ public String getValueByName(ArrayList<FieldRow> aRows, int aRowOffset, String aName) { FieldRow fieldRow = aRows.get(aRowOffset); if (fieldRow != null) return getValueByName(fieldRow, aName); else return StringUtils.EMPTY; } /** * Returns a <i>DataField</i> representing the cell contained within * the array of field rows at the specified row offset and matching the * column name. * * @param aRows Array list of field rows. * @param aRowOffset Row offset into the array list. * @param aName Name of the column. * * @return Data field or <i>null</i> if not found. */ public DataField getFieldByName(ArrayList<FieldRow> aRows, int aRowOffset, String aName) { DataField dataField = null; FieldRow fieldRow = aRows.get(aRowOffset); if (fieldRow != null) { int colOffset = mColumns.getOffsetByName(aName); dataField = new DataField(mColumns.getByOffset(colOffset)); dataField.setValues(fieldRow.getValues(colOffset)); } return dataField; } /** * Process the table information (name, columns, features, rows) * through the digital hash algorithm. * * @param aHash Digital hash instance. * @param anIsFeatureIncluded Should features be included. * * @throws IOException Triggered by hash algorithm. */ public void processHash(DigitalHash aHash, boolean anIsFeatureIncluded) throws IOException { DataBag rowBag; aHash.processBuffer(getName()); if (anIsFeatureIncluded) { for (Map.Entry<String, String> featureEntry : getFeatures().entrySet()) { aHash.processBuffer(featureEntry.getKey()); aHash.processBuffer(featureEntry.getValue()); } } int rowCount = rowCount(); for (int row = 0; row < rowCount; row++) { rowBag = getRowAsBag(row); rowBag.processHash(aHash, anIsFeatureIncluded); } } /** * Generates a unique hash string using the MD5 algorithm using * the table information. * * @param anIsFeatureIncluded Should feature name/values be included? * * @return Unique hash string. */ public String generateUniqueHash(boolean anIsFeatureIncluded) { String hashId; DataBag rowBag; DigitalHash digitalHash = new DigitalHash(); try { processHash(digitalHash, anIsFeatureIncluded); hashId = digitalHash.getHashSequence(); } catch (IOException e) { UUID uniqueId = UUID.randomUUID(); hashId = uniqueId.toString(); } return hashId; } /** * Returns one or more field rows that match the search criteria of the * parameters provided. Each row of the table will be examined to determine * if a cell identified by name evaluates true when the operator and value * are applied to it. * <p> * <b>Note:</b> This method supports text based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue Comparison value. * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValue(String aName, Field.Operator anOperator, String aValue) { String valueString; FieldRow fieldRow; Matcher regexMatcher = null; Pattern regexPattern = null; ArrayList<FieldRow> matchingRows = new ArrayList<FieldRow>(); int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((aValue != null) && (colOffset != -1) && (rowCount > 0)) { for (int row = 0; row < rowCount; row++) { fieldRow = getRow(row); valueString = fieldRow.getValue(colOffset); switch (anOperator) { case NOT_EMPTY: if (StringUtils.isNotEmpty(valueString)) matchingRows.add(fieldRow); break; case EQUAL: if (StringUtils.equals(valueString, aValue)) matchingRows.add(fieldRow); break; case NOT_EQUAL: if (!StringUtils.equals(valueString, aValue)) matchingRows.add(fieldRow); break; case CONTAINS: if (StringUtils.contains(valueString, aValue)) matchingRows.add(fieldRow); break; case STARTS_WITH: if (StringUtils.startsWith(valueString, aValue)) matchingRows.add(fieldRow); break; case ENDS_WITH: if (StringUtils.endsWith(valueString, aValue)) matchingRows.add(fieldRow); break; case EMPTY: if (StringUtils.isEmpty(valueString)) matchingRows.add(fieldRow); break; case REGEX: // http://www.regular-expressions.info/java.html if (regexPattern == null) regexPattern = Pattern.compile(aValue); if (regexMatcher == null) regexMatcher = regexPattern.matcher(valueString); else regexMatcher.reset(valueString); if (regexMatcher.find()) matchingRows.add(fieldRow); break; } } } return matchingRows; } /** * Returns one or more field rows that match the search criteria of the * parameters provided in a case insensitive manner. Each row of the * table will be examined to determine if a cell identified by name * evaluates true when the operator and value are applied to it. * <p> * <b>Note:</b> This method supports text based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue Comparison value. * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValueInsensitive(String aName, Field.Operator anOperator, String aValue) { FieldRow fieldRow; String valueString; Matcher regexMatcher = null; Pattern regexPattern = null; ArrayList<FieldRow> matchingRows = new ArrayList<FieldRow>(); int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((aValue != null) && (colOffset != -1) && (rowCount > 0)) { for (int row = 0; row < rowCount; row++) { fieldRow = getRow(row); valueString = fieldRow.getValue(colOffset); switch (anOperator) { case EQUAL: if (StringUtils.equalsIgnoreCase(valueString, aValue)) matchingRows.add(fieldRow); break; case NOT_EQUAL: if (!StringUtils.equalsIgnoreCase(valueString, aValue)) matchingRows.add(fieldRow); break; case CONTAINS: if (StringUtils.containsIgnoreCase(valueString, aValue)) matchingRows.add(fieldRow); break; case STARTS_WITH: if (StringUtils.startsWithIgnoreCase(valueString, aValue)) matchingRows.add(fieldRow); break; case ENDS_WITH: if (StringUtils.endsWithIgnoreCase(valueString, aValue)) matchingRows.add(fieldRow); break; case EMPTY: if (StringUtils.isEmpty(valueString)) matchingRows.add(fieldRow); break; case REGEX: // http://www.regular-expressions.info/java.html if (regexPattern == null) regexPattern = Pattern.compile(aValue, Pattern.CASE_INSENSITIVE); if (regexMatcher == null) regexMatcher = regexPattern.matcher(valueString); else regexMatcher.reset(valueString); if (regexMatcher.find()) matchingRows.add(fieldRow); break; } } } return matchingRows; } /** * Returns one or more field rows that match the search criteria of the * parameters provided in a case insensitive manner. Each row of the * table will be examined to determine if a cell identified by name * evaluates true when the operator and value(s) are applied to it. * <p> * <b>Note:</b> This method supports numeric based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue1 Primary comparison value. * @param aValue2 Secondary comparison value (ignored unless the logical * operator is <i>Field.Operator.BETWEEN</i>). * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValue(String aName, Field.Operator anOperator, int aValue1, int aValue2) { int nativeValue; FieldRow fieldRow; ArrayList<FieldRow> matchingRows = new ArrayList<FieldRow>(); int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((colOffset != -1) && (rowCount > 0)) { DataField dataField = mColumns.getByOffset(colOffset); for (int row = 0; row < rowCount; row++) { fieldRow = getRow(row); if (dataField.isTypeNumber()) { nativeValue = Field.createInt(fieldRow.getValue(colOffset)); switch (anOperator) { case EQUAL: if (nativeValue == aValue1) matchingRows.add(fieldRow); break; case NOT_EQUAL: if (nativeValue != aValue1) matchingRows.add(fieldRow); break; case GREATER_THAN: if (nativeValue > aValue1) matchingRows.add(fieldRow); break; case GREATER_THAN_EQUAL: if (nativeValue >= aValue1) matchingRows.add(fieldRow); break; case LESS_THAN: if (nativeValue < aValue1) matchingRows.add(fieldRow); break; case LESS_THAN_EQUAL: if (nativeValue <= aValue1) matchingRows.add(fieldRow); break; case BETWEEN: if ((nativeValue > aValue1) && (nativeValue < aValue2)) matchingRows.add(fieldRow); break; case BETWEEN_INCLUSIVE: if ((nativeValue >= aValue1) && (nativeValue <= aValue2)) matchingRows.add(fieldRow); break; case EMPTY: if (StringUtils.isEmpty(fieldRow.getValue(colOffset))) matchingRows.add(fieldRow); break; } } } } return matchingRows; } /** * Returns one or more field rows that match the search criteria of the * parameters provided in a case insensitive manner. Each row of the * table will be examined to determine if a cell identified by name * evaluates true when the operator and value(s) are applied to it. * <p> * <b>Note:</b> This method supports numeric based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue1 Primary comparison value. * @param aValue2 Secondary comparison value (ignored unless the logical * operator is <i>Field.Operator.BETWEEN</i>). * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValue(String aName, Field.Operator anOperator, long aValue1, long aValue2) { long nativeValue; FieldRow fieldRow; ArrayList<FieldRow> matchingRows = new ArrayList<FieldRow>(); int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((colOffset != -1) && (rowCount > 0)) { DataField dataField = mColumns.getByOffset(colOffset); for (int row = 0; row < rowCount; row++) { fieldRow = getRow(row); if ((dataField.isTypeNumber()) || (dataField.isTypeDateOrTime())) { nativeValue = Field.createLong(fieldRow.getValue(colOffset)); switch (anOperator) { case EQUAL: if (nativeValue == aValue1) matchingRows.add(fieldRow); break; case NOT_EQUAL: if (nativeValue != aValue1) matchingRows.add(fieldRow); break; case GREATER_THAN: if (nativeValue > aValue1) matchingRows.add(fieldRow); break; case GREATER_THAN_EQUAL: if (nativeValue >= aValue1) matchingRows.add(fieldRow); break; case LESS_THAN: if (nativeValue < aValue1) matchingRows.add(fieldRow); break; case LESS_THAN_EQUAL: if (nativeValue <= aValue1) matchingRows.add(fieldRow); break; case BETWEEN: if ((nativeValue > aValue1) && (nativeValue < aValue2)) matchingRows.add(fieldRow); break; case BETWEEN_INCLUSIVE: if ((nativeValue >= aValue1) && (nativeValue <= aValue2)) matchingRows.add(fieldRow); break; case EMPTY: if (StringUtils.isEmpty(fieldRow.getValue(colOffset))) matchingRows.add(fieldRow); break; } } } } return matchingRows; } /** * Returns one or more field rows that match the search criteria of the * parameters provided in a case insensitive manner. Each row of the * table will be examined to determine if a cell identified by name * evaluates true when the operator and value(s) are applied to it. * <p> * <b>Note:</b> This method supports date/time based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue1 Primary comparison value. * @param aValue2 Secondary comparison value (ignored unless the logical * operator is <i>Field.Operator.BETWEEN</i>). * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValue(String aName, Field.Operator anOperator, Date aValue1, Date aValue2) { long nativeValue; FieldRow fieldRow; long longValue1 = aValue1.getTime(); long longValue2 = aValue2.getTime(); ArrayList<FieldRow> matchingRows = new ArrayList<FieldRow>(); int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((colOffset != -1) && (rowCount > 0)) { DataField dataField = mColumns.getByOffset(colOffset); for (int row = 0; row < rowCount; row++) { fieldRow = getRow(row); if (dataField.isTypeDateOrTime()) { nativeValue = Field.createDate(fieldRow.getValue(colOffset)).getTime(); switch (anOperator) { case EQUAL: if (nativeValue == longValue1) matchingRows.add(fieldRow); break; case NOT_EQUAL: if (nativeValue != longValue1) matchingRows.add(fieldRow); break; case GREATER_THAN: if (nativeValue > longValue1) matchingRows.add(fieldRow); break; case GREATER_THAN_EQUAL: if (nativeValue >= longValue1) matchingRows.add(fieldRow); break; case LESS_THAN: if (nativeValue < longValue1) matchingRows.add(fieldRow); break; case LESS_THAN_EQUAL: if (nativeValue <= longValue1) matchingRows.add(fieldRow); break; case BETWEEN: if ((nativeValue > longValue1) && (nativeValue < longValue2)) matchingRows.add(fieldRow); break; case BETWEEN_INCLUSIVE: if ((nativeValue >= longValue1) && (nativeValue <= longValue2)) matchingRows.add(fieldRow); break; case EMPTY: if (StringUtils.isEmpty(fieldRow.getValue(colOffset))) matchingRows.add(fieldRow); break; } } } } return matchingRows; } /** * Returns one or more field rows that match the search criteria of the * parameters provided in a case insensitive manner. Each row of the * table will be examined to determine if a cell identified by name * evaluates true when the operator and value(s) are applied to it. * <p> * <b>Note:</b> This method supports numeric based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue1 Primary comparison value. * @param aValue2 Secondary comparison value (ignored unless the logical * operator is <i>Field.Operator.BETWEEN</i>). * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValue(String aName, Field.Operator anOperator, double aValue1, double aValue2) { double nativeValue; FieldRow fieldRow; ArrayList<FieldRow> matchingRows = new ArrayList<FieldRow>(); int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((colOffset != -1) && (rowCount > 0)) { DataField dataField = mColumns.getByOffset(colOffset); for (int row = 0; row < rowCount; row++) { fieldRow = getRow(row); if (dataField.isTypeNumber()) { nativeValue = Field.createDouble(fieldRow.getValue(colOffset)); switch (anOperator) { case EQUAL: if (nativeValue == aValue1) matchingRows.add(fieldRow); break; case NOT_EQUAL: if (nativeValue != aValue1) matchingRows.add(fieldRow); break; case GREATER_THAN: if (nativeValue > aValue1) matchingRows.add(fieldRow); break; case GREATER_THAN_EQUAL: if (nativeValue >= aValue1) matchingRows.add(fieldRow); break; case LESS_THAN: if (nativeValue < aValue1) matchingRows.add(fieldRow); break; case LESS_THAN_EQUAL: if (nativeValue <= aValue1) matchingRows.add(fieldRow); break; case BETWEEN: if ((nativeValue > aValue1) && (nativeValue < aValue2)) matchingRows.add(fieldRow); break; case BETWEEN_INCLUSIVE: if ((nativeValue >= aValue1) && (nativeValue <= aValue2)) matchingRows.add(fieldRow); break; case EMPTY: if (StringUtils.isEmpty(fieldRow.getValue(colOffset))) matchingRows.add(fieldRow); break; } } } } return matchingRows; } /** * Convenience method that retrieves the first row in an array as * a bag. * <p> * <b>Note:</b>The bag is a cloned copy of the table row and * should not be used to update the table. Instead, you should * use <code>Field.firstFieldRow()</code> and perform updates via * the <i>FieldRow</i> instance. * </p> * * @param aFieldRows Array of field rows. * * @return Bag of fields representing the field row. */ public DataBag firstRowAsBag(ArrayList<FieldRow> aFieldRows) { if ((aFieldRows != null) && (aFieldRows.size() > 0)) return getRowAsBag(aFieldRows.get(0)); else return null; } /** * Returns one or more field rows that match the search criteria of the * parameters provided in a case insensitive manner. Each row of the * table will be examined to determine if a cell identified by name * evaluates true when the operator and value(s) are applied to it. * <p> * <b>Note:</b> This method supports numeric based logical operators only. * You should use other <code>findValue()</code> methods for different * data types. * </p> * * @param aName Column name. * @param anOperator Logical operator. * @param aValue1 Primary comparison value. * @param aValue2 Secondary comparison value (ignored unless the logical * operator is <i>Field.Operator.BETWEEN</i>). * * @return Array list of matching field rows or <i>null</i> if none evaluate true. */ public ArrayList<FieldRow> findValue(String aName, Field.Operator anOperator, float aValue1, float aValue2) { double doubleValue1 = (double) aValue1; double doubleValue2 = (double) aValue2; return findValue(aName, anOperator, doubleValue1, doubleValue2); } final Comparator<FieldRow> ROW_SORT_ASCENDING = new Comparator<FieldRow>() { public int compare(FieldRow aRow1, FieldRow aRow2) { int colOffset = offsetByName(mSortFieldName); DataField dataField = mColumns.getByOffset(colOffset); String cellValue1 = aRow1.getValue(colOffset); String cellValue2 = aRow2.getValue(colOffset); switch (dataField.getType()) { case Integer: Integer integerValue1 = Integer.valueOf(cellValue1); Integer integerValue2 = Integer.valueOf(cellValue2); return integerValue1.compareTo(integerValue2); case Long: Long longValue1 = Long.valueOf(cellValue1); Long longValue2 = Long.valueOf(cellValue2); return longValue1.compareTo(longValue2); case Float: Float floatValue1 = Float.valueOf(cellValue1); Float floatValue2 = Float.valueOf(cellValue2); return floatValue1.compareTo(floatValue2); case Double: Double doubleValue1 = Double.valueOf(cellValue1); Double doubleValue2 = Double.valueOf(cellValue2); return doubleValue1.compareTo(doubleValue2); case Boolean: Boolean booleanValue1 = Field.isValueTrue(cellValue1); Boolean booleanValue2 = Field.isValueTrue(cellValue2); return booleanValue1.compareTo(booleanValue2); case Date: case Time: case DateTime: Date dateValue1 = Field.createDate(cellValue1); Date dateValue2 = Field.createDate(cellValue2); return dateValue1.compareTo(dateValue2); default: return cellValue1.compareToIgnoreCase(cellValue2); } } }; final Comparator<FieldRow> ROW_SORT_DESCENDING = new Comparator<FieldRow>() { public int compare(FieldRow aRow1, FieldRow aRow2) { int colOffset = offsetByName(mSortFieldName); DataField dataField = mColumns.getByOffset(colOffset); String cellValue1 = aRow1.getValue(colOffset); String cellValue2 = aRow2.getValue(colOffset); switch (dataField.getType()) { case Integer: Integer integerValue1 = Integer.valueOf(cellValue1); Integer integerValue2 = Integer.valueOf(cellValue2); return integerValue2.compareTo(integerValue1); case Long: Long longValue1 = Long.valueOf(cellValue1); Long longValue2 = Long.valueOf(cellValue2); return longValue2.compareTo(longValue1); case Float: Float floatValue1 = Float.valueOf(cellValue1); Float floatValue2 = Float.valueOf(cellValue2); return floatValue2.compareTo(floatValue1); case Double: Double doubleValue1 = Double.valueOf(cellValue1); Double doubleValue2 = Double.valueOf(cellValue2); return doubleValue2.compareTo(doubleValue1); case Boolean: Boolean booleanValue1 = Field.isValueTrue(cellValue1); Boolean booleanValue2 = Field.isValueTrue(cellValue2); return booleanValue2.compareTo(booleanValue1); case Date: case Time: case DateTime: Date dateValue1 = Field.createDate(cellValue1); Date dateValue2 = Field.createDate(cellValue2); return dateValue2.compareTo(dateValue1); default: return cellValue2.compareToIgnoreCase(cellValue1); } } }; /** * Sorts the rows of the table based on the column name and sort * order parameters. * * @param aName Column name. * @param anOrder Sort order (ascending, descending). */ public void sortByColumn(String aName, Field.Order anOrder) { int rowCount = rowCount(); int colOffset = offsetByName(aName); if ((colOffset != -1) && (rowCount > 1) && (anOrder != Field.Order.UNDEFINED)) { mSortFieldName = aName; if (anOrder == Field.Order.ASCENDING) Collections.sort(mRows, ROW_SORT_ASCENDING); else Collections.sort(mRows, ROW_SORT_DESCENDING); mSortFieldName = StringUtils.EMPTY; } } /** * Add a unique feature to this table. A feature enhances the core * capability of the table. Standard features are listed below. * <ul> * <li>Field.FEATURE_OPERATION_NAME</li> * </ul> * * @param aName Name of the feature. * @param aValue Value to associate with the feature. */ public void addFeature(String aName, String aValue) { mFeatures.put(aName, aValue); } /** * Add a unique feature to this table. A feature enhances the core * capability of the table. * * @param aName Name of the feature. * @param aValue Value to associate with the feature. */ public void addFeature(String aName, int aValue) { addFeature(aName, Integer.toString(aValue)); } /** * Enabling the feature will add the name and assign it a * value of <i>StrUtl.STRING_TRUE</i>. * * @param aName Name of the feature. */ public void enableFeature(String aName) { mFeatures.put(aName, StrUtl.STRING_TRUE); } /** * Disabling a feature will remove its name and value * from the internal list. * * @param aName Name of feature. */ public void disableFeature(String aName) { mFeatures.remove(aName); } /** * Returns <i>true</i> if the feature was previously * added and assigned a value. * * @param aName Name of feature. * * @return <i>true</i> or <i>false</i> */ public boolean isFeatureAssigned(String aName) { return (getFeature(aName) != null); } /** * Returns <i>true</i> if the feature was previously * added and assigned a value of <i>StrUtl.STRING_TRUE</i>. * * @param aName Name of feature. * * @return <i>true</i> or <i>false</i> */ public boolean isFeatureTrue(String aName) { return StrUtl.stringToBoolean(mFeatures.get(aName)); } /** * Returns <i>true</i> if the feature was previously * added and not assigned a value of <i>StrUtl.STRING_TRUE</i>. * * @param aName Name of feature. * * @return <i>true</i> or <i>false</i> */ public boolean isFeatureFalse(String aName) { return !StrUtl.stringToBoolean(mFeatures.get(aName)); } /** * Returns <i>true</i> if the feature was previously * added and its value matches the one provided as a * parameter. * * @param aName Feature name. * @param aValue Feature value to match. * * @return <i>true</i> or <i>false</i> */ public boolean isFeatureEqual(String aName, String aValue) { String featureValue = getFeature(aName); return StringUtils.equalsIgnoreCase(featureValue, aValue); } /** * Count of unique features assigned to this table. * * @return Feature count. */ public int featureCount() { return mFeatures.size(); } /** * Returns the String associated with the feature name or * <i>null</i> if the name could not be found. * * @param aName Feature name. * * @return Feature value or <i>null</i> */ public String getFeature(String aName) { return mFeatures.get(aName); } /** * Returns the int associated with the feature name. * * @param aName Feature name. * * @return Feature value or <i>null</i> */ public int getFeatureAsInt(String aName) { return Field.createInt(getFeature(aName)); } /** * Removes all features assigned to this object instance. */ public void clearFeatures() { mFeatures.clear(); } /** * Assigns the hash map of features to the list. * * @param aFeatures Feature list. */ public void setFeatures(HashMap<String, String> aFeatures) { if (aFeatures != null) mFeatures = new HashMap<String, String>(aFeatures); } /** * Indicates whether some other object is "equal to" this one. * * @param anObject Reference object with which to compare. * @return {@code true} if this object is the same as the anObject * argument; {@code false} otherwise. */ @Override public boolean equals(Object anObject) { if (this == anObject) return true; if (anObject == null || getClass() != anObject.getClass()) return false; DataTable dataTable = (DataTable) anObject; if (mColumns != null ? !mColumns.equals(dataTable.mColumns) : dataTable.mColumns != null) return false; if (mRows != null ? !mRows.equals(dataTable.mRows) : dataTable.mRows != null) return false; if (mName != null ? !mName.equals(dataTable.mName) : dataTable.mName != null) return false; return !(mFeatures != null ? !mFeatures.equals(dataTable.mFeatures) : dataTable.mFeatures != null); } /** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. * * @return A hash code value for this object. */ @Override public int hashCode() { int result = mColumns != null ? mColumns.hashCode() : 0; result = 31 * result + (mRows != null ? mRows.hashCode() : 0); result = 31 * result + (mName != null ? mName.hashCode() : 0); result = 31 * result + (mFeatures != null ? mFeatures.hashCode() : 0); return result; } /** * Returns a read-only copy of the internal map containing * feature list. * * @return Internal feature map instance. */ public final HashMap<String, String> getFeatures() { return mFeatures; } /** * Add an application defined property to the table. * <p> * <b>Notes:</b> * </p> * <ul> * <li>The goal of the DataTable is to strike a balance between * providing enough properties to adequately model application * related data without overloading it.</li> * <li>This method offers a mechanism to capture additional * (application specific) properties that may be needed.</li> * <li>Properties added with this method are transient and * will not be stored when saved.</li> * </ul> * * @param aName Property name (duplicates are not supported). * @param anObject Instance of an object. */ public void addProperty(String aName, Object anObject) { if (mProperties == null) mProperties = new HashMap<String, Object>(); mProperties.put(aName, anObject); } /** * Returns the object associated with the property name or * <i>null</i> if the name could not be matched. * * @param aName Name of the property. * * @return Instance of an object. */ public Object getProperty(String aName) { if (mProperties == null) return null; else return mProperties.get(aName); } /** * Removes all application defined properties assigned to this table. */ public void clearProperties() { if (mProperties != null) mProperties.clear(); } /** * Returns the property map instance managed by the table or <i>null</i> * if empty. * * @return Hash map instance. */ public HashMap<String, Object> getProperties() { return mProperties; } }