com.hfg.util.collection.DataTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.util.collection;
import com.hfg.exception.DataParsingException;
import com.hfg.html.HTMLTag;
import com.hfg.html.Span;
import com.hfg.javascript.JsArray;
import com.hfg.javascript.JsCollection;
import com.hfg.javascript.JsObjMap;
import com.hfg.util.AttributeMgr;
import com.hfg.util.StringUtil;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
//------------------------------------------------------------------------------
/**
Table containing sets of subject metadata.
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class DataTable extends AbstractSparseMatrix
{
private String mName;
private Set mFlags;
private DataColumn mRowKeyCol;
private AttributeMgr mAttributeMgr;
//##########################################################################
// CONSTRUCTORS
//##########################################################################
//--------------------------------------------------------------------------
public DataTable()
{
super();
}
//--------------------------------------------------------------------------
public DataTable(int inInitialRowCapacity, int inInitialColCapacity)
{
super(inInitialRowCapacity, inInitialColCapacity);
}
//--------------------------------------------------------------------------
public DataTable(JsArray inJsonArray)
{
this(inJsonArray, null);
}
//--------------------------------------------------------------------------
/**
* Constructor intended to take a JSON array of rows (as maps).
* @param inJsonArray a JSON array object
* @param inRowKeyCol an optional column to use as the row key.
* If a value is not provided, the row number is used as the row key.
*/
public DataTable(JsArray inJsonArray, String inRowKeyCol)
{
this();
if (CollectionUtil.hasValues(inJsonArray))
{
int rowCount = 0;
for (JsCollection row : (Collection) (Object) inJsonArray)
{
if (row instanceof JsObjMap)
{
rowCount++;
JsObjMap rowMap = (JsObjMap) row;
String rowKey;
if (inRowKeyCol != null)
{
rowKey = rowMap.getString(inRowKeyCol);
}
else
{
rowKey = rowCount + "";
}
for (String key : rowMap.keySet())
{
if (inRowKeyCol != null
&& inRowKeyCol.equals(key))
{
continue; // Don't create a column for the row key
}
Object value = rowMap.get(key);
if (value instanceof Comparable)
{
DataColumn col = getDataColumn(key);
if (null == col)
{
col = new DataColumn(key);
}
put(rowKey, col, (Comparable) value);
}
}
}
}
}
}
//---------------------------------------------------------------------------
public DataTable(List inParsedLines)
throws IOException
{
Map colMap = new HashMap<>(10);
boolean headerParsed = false;
int rowIndex = 0;
for (String[] fields : inParsedLines)
{
if (! headerParsed)
{
boolean hasData = StringUtil.isSet(fields[0]);
if ((1 == fields.length && ! hasData) // Skip blank lines
|| (hasData && fields[0].startsWith("#"))) // Skip comment lines
{
continue;
}
for (int i = 0; i < fields.length; i++)
{
String field = fields[i];
if (field != null)
{
field = field.trim();
}
else
{
field = "Col " + (i + 1);
}
DataColumn col = new DataColumn(field);
colMap.put(i, col);
}
headerParsed = true;
}
else
{
rowIndex++;
if (fields.length > colMap.size())
{
throw new DataParsingException("Row " + rowIndex + " has more fields (" + fields.length + ") than the number of columns (" + colMap.size() + ")!");
}
for (int i = 0; i < fields.length; i++)
{
String fieldString = fields[i];
Comparable field = null;
if (fieldString != null)
{
fieldString = fieldString.trim();
if (StringUtil.isNumber(fieldString))
{
try
{
if (fieldString.contains("."))
{
field = Double.parseDouble(fieldString);
}
else if (fieldString.length() > 9)
{
field = Long.parseLong(fieldString);
}
else
{
field = Integer.parseInt(fieldString);
}
}
catch (NumberFormatException e)
{
field = fieldString;
}
}
else
{
field = fieldString;
}
}
put(rowIndex + "", colMap.get(i), field);
}
}
}
}
//##########################################################################
// PUBLIC METHODS
//##########################################################################
//--------------------------------------------------------------------------
public String name()
{
return mName;
}
//--------------------------------------------------------------------------
public DataTable setName(String inValue)
{
mName = inValue;
return this;
}
//--------------------------------------------------------------------------
public DataTable setRowKeyCol(DataColumn inValue)
{
mRowKeyCol = inValue;
return this;
}
//--------------------------------------------------------------------------
public DataColumn getRowKeyCol()
{
return mRowKeyCol;
}
//--------------------------------------------------------------------------
public DataTable setFlag(String inValue)
{
if (null == mFlags)
{
mFlags = new HashSet<>(4);
}
mFlags.add(inValue);
return this;
}
//--------------------------------------------------------------------------
public boolean hasFlag(String inValue)
{
return mFlags != null && mFlags.contains(inValue);
}
//--------------------------------------------------------------------------
public void clearFlags()
{
mFlags = null;
}
//--------------------------------------------------------------------------
public void clearFlag(String inValue)
{
if (mFlags != null)
{
mFlags.remove(inValue);
}
}
//--------------------------------------------------------------------------
public DataTable add(DataTable inDataTable)
{
for (DataColumn dataColumn : inDataTable.getDataColumns())
{
addDataSet(dataColumn, inDataTable.getDataSet(dataColumn));
}
return this;
}
//--------------------------------------------------------------------------
public DataTable addDataSet(DataColumn inDataColumn, Map inDataMap)
{
for (String subjectId : inDataMap.keySet())
{
put(subjectId, inDataColumn, inDataMap.get(subjectId));
}
return this;
}
//--------------------------------------------------------------------------
@Override
public DataRow getRow(String inRowKey)
{
Map colMap = super.getRow(inRowKey);
return (colMap != null ? new DataRow(colMap) : null);
}
//--------------------------------------------------------------------------
public List getRows()
{
List rows = new ArrayList<>(rowKeySet().size());
for (String rowKey : rowKeySet())
{
rows.add(getRow(rowKey));
}
return rows;
}
//--------------------------------------------------------------------------
public String getFirstRowKey()
/* Return the first Row Key Mainly used for single row DataTables to help find data*/
{
Optional firstRowKey = rowKeySet().stream().findFirst();
return firstRowKey.isPresent() ? firstRowKey.get() : null;
}
//--------------------------------------------------------------------------
/* Make a new DataTable for the Rows that match the RowKey */
public DataTable getRows(String inRowKey)
{
DataRow dataRows = getRow(inRowKey);
if (dataRows.size() == 0 ) { return null;}
return makeDataTableFromDataRows(dataRows, inRowKey);
}
//--------------------------------------------------------------------------
public DataTable makeDataTableFromDataRows(DataRow inDataRow, String inRowKey)
{
int rowCount = 1;
int colCount = inDataRow.getDataColumns().size();
DataTable dataTable = new DataTable(rowCount, colCount);
dataTable.putRow(inRowKey, inDataRow);
return dataTable;
}
//--------------------------------------------------------------------------
public DataTable makeDataTableMatchingColumnValues(DataColumn inDataColumn, V inQuery)
/* Search a column within a DataTable, return a new DataTable with all the matching rows
* Return Empty DataTable if no values are found
* */
{
List foundRowKeys = this.getRowKeysMatchingValues(inDataColumn, inQuery);
int rowCount = foundRowKeys.size();
int colCount = colKeySet().size();
DataTable dataTable = new DataTable(rowCount, colCount);
for (String rowKey: foundRowKeys )
{
dataTable.putRow(rowKey, this.getRow(rowKey));
}
return dataTable;
}
//--------------------------------------------------------------------------
public Map getDataSet(DataColumn inDataColumn)
{
return getCol(inDataColumn);
}
//--------------------------------------------------------------------------
public List getRowKeysMatchingValues(DataColumn inDataColumn, V inQuery )
/* Search a column for a value and return a unique list of all the RowsKeys found with the query value
* */
{
//Get a Map of all the column values: Key is RowKey
Map colMap = getDataSet(inDataColumn);
return colMap.entrySet()
.stream()
.filter(entry -> entry.getValue().equals(inQuery))
.map(entry -> (T) entry.getKey())
.distinct()
.collect(Collectors.toList());
}
//--------------------------------------------------------------------------
public void updateColumnTitle(DataColumn inColumn, String inNewTitle)
{
DataColumn newDataColumn = inColumn.clone().setTitle(inNewTitle);
changeColKey(inColumn, newDataColumn);
}
//--------------------------------------------------------------------------
public Set getDataColumns()
{
return colKeySet();
}
//--------------------------------------------------------------------------
public DataColumn getDataColumn(String inName)
{
DataColumn requestedCol = null;
Set dataColumns = getDataColumns();
if (CollectionUtil.hasValues(dataColumns))
{
for (DataColumn col : dataColumns)
{
if (col.getTitle().equalsIgnoreCase(inName))
{
requestedCol = col;
break;
}
}
}
return requestedCol;
}
//--------------------------------------------------------------------------
@Override
public void removeCol(DataColumn inCol)
{
super.removeCol(inCol);
}
//--------------------------------------------------------------------------
@Override
public Set rowKeySet()
{
return super.rowKeySet();
}
//--------------------------------------------------------------------------
/**
* Copies the row data for the specified columns into a new DataTable.
* @param inColumns the columns to include
* @return the DataTable copy
*/
public DataTable slice(DataColumn... inColumns)
{
ArrayList columns = new ArrayList<>(inColumns.length);
for (DataColumn col : inColumns)
{
columns.add(col);
}
return slice(columns);
}
//--------------------------------------------------------------------------
/**
* Copies the row data for the specified columns into a new DataTable.
* @param inColumns the columns to include
* @return the DataTable copy
*/
public DataTable slice(Collection inColumns)
{
DataTable slice = (DataTable) clone();
for (DataColumn col : getDataColumns())
{
if (! inColumns.contains(col))
{
slice.removeCol(col);
}
}
return slice;
}
//--------------------------------------------------------------------------
/**
* Copies the row data for columns other than the specified columns into a new DataTable.
* @param inColumns the columns to exclude
* @return the DataTable copy
*/
public DataTable inverseSlice(DataColumn... inColumns)
{
ArrayList columns = new ArrayList<>(inColumns.length);
for (DataColumn col : inColumns)
{
columns.add(col);
}
return inverseSlice(columns);
}
//--------------------------------------------------------------------------
/**
* Copies the row data for columns other than the specified columns into a new DataTable.
* @param inColumns the columns to exclude
* @return the DataTable copy
*/
public DataTable inverseSlice(Collection inColumns)
{
DataTable slice = (DataTable) clone();
for (DataColumn col : getDataColumns())
{
if (inColumns.contains(col))
{
slice.removeCol(col);
}
}
return slice;
}
//--------------------------------------------------------------------------
/**
Uses a formatting string (like "%.1f%%") specified via the DataColumn to
render the object value for display.
* @param inId subject key
* @param inDataColumn the data column from which to retrieve the value
* @return the String-formatted value
*/
public String getFormatted(String inId, DataColumn inDataColumn)
{
String formattedValue = "";
Object value = get(inId, inDataColumn);
if (value != null)
{
if (inDataColumn.getFormatString() != null)
{
formattedValue = String.format(inDataColumn.getFormatString(), value);
}
else
{
formattedValue = value.toString();
}
}
return formattedValue;
}
//--------------------------------------------------------------------------
/**
Uses a formatting string (like "%.1f%%") specified via the DataColumn to
render the object value for display.
* @param inId subject key
* @param inDataColumn the data column from which to retrieve the value
* @return the String-formatted value
*/
public HTMLTag getHTMLFormatted(String inId, DataColumn inDataColumn)
{
Object value = get(inId, inDataColumn);
HTMLTag formattedValue;
try
{
Method method = value.getClass().getMethod("toHTMLTag");
formattedValue = (HTMLTag) method.invoke(value);
}
catch (Exception e)
{
// Ignore exceptions. Just use the string value.
formattedValue = new Span(getFormatted(inId, inDataColumn));
}
return formattedValue;
}
//--------------------------------------------------------------------------
public void setAttribute(String inName, Object inValue)
{
getOrInitAttributeMgr().setAttribute(inName, inValue);
}
//--------------------------------------------------------------------------
public boolean hasAttributes()
{
return mAttributeMgr != null && mAttributeMgr.hasAttributes();
}
//--------------------------------------------------------------------------
public boolean hasAttribute(String inName)
{
return mAttributeMgr != null && getOrInitAttributeMgr().hasAttribute(inName);
}
//--------------------------------------------------------------------------
public Object getAttribute(String inName)
{
return getOrInitAttributeMgr().getAttribute(inName);
}
//--------------------------------------------------------------------------
public Collection getAttributeNames()
{
return getOrInitAttributeMgr().getAttributeNames();
}
//--------------------------------------------------------------------------
public void clearAttributes()
{
if (mAttributeMgr != null)
{
mAttributeMgr.clearAttributes();
}
}
//--------------------------------------------------------------------------
public Object removeAttribute(String inName)
{
Object attr = null;
if (mAttributeMgr != null)
{
attr = getOrInitAttributeMgr().removeAttribute(inName);
}
return attr;
}
//--------------------------------------------------------------------------
public Boolean allColValuesMatch(DataColumn inCol)
{
List colValues = colValues(inCol);
return colValues.stream().allMatch(i -> i.equals(colValues.get(0)));
}
//--------------------------------------------------------------------------
public List colValues(DataColumn inCol)
{
Map orderedDataSet = this.getDataSet(inCol);
List colVals = (List) orderedDataSet.values();
return colVals;
}
//--------------------------------------------------------------------------
public Long countUnique(DataColumn inCol)
{
List colValues = colValues(inCol);
return colValues.stream().distinct().count();
}
//--------------------------------------------------------------------------
public List getUniqueValues(DataColumn inCol)
/*
Return a list of all values in column.
Will attempt to Cast to the wanted Type when collecting data
*/
{
Supplier> supplier = ArrayList::new;
return colValues(inCol).stream()
.map(s -> (T) s)
.distinct()
.collect(Collectors.toCollection(supplier));
}
//##########################################################################
// PRIVATE METHODS
//##########################################################################
//--------------------------------------------------------------------------
private AttributeMgr getOrInitAttributeMgr()
{
if (null == mAttributeMgr)
{
mAttributeMgr = new AttributeMgr();
}
return mAttributeMgr;
}
}