Table.java [src/soils/db/tables] Revision: default Date:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package soils.db.tables;
import csip.api.server.ServiceException;
import static csip.utils.JSONUtils.preprocess;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import soils.exceptions.SDMException;
/**
* Abstract base class for all subsequent database tables.
*
* @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
*/
public abstract class Table {
/**
* This ConcurrentHashMap contains all of the columns made available for this
* table to be used by any calling programs. This list can be added to over
* time if the database is edited or if columns were initially left out of the
* list because no calling programs were currently using them. An
* implementations of this class should use the addDataColumn function to add
* columns to the list. This list is final because it should only be
* created/modified once during construction of the implementing class.
*
* @see Table#addDataColumn
*/
protected final ConcurrentHashMap<String, TableColumn> columns = new ConcurrentHashMap<>();
protected ArrayList<String> outputOrderList = new ArrayList<>();
protected String tableName = "";
protected String schemaName = "";
protected ArrayList<String> getMandatoryColumns() {
return null;
}
public synchronized void deepCopy(Table newTable) {
if (null != newTable) {
ConcurrentHashMap<String, TableColumn> newColumns = newTable.getColumns();
for (String key : columns.keySet()) {
if (newColumns.containsKey(key)) {
newColumns.get(key).setValue(columns.get(key).value);
}
}
}
}
public ConcurrentHashMap<String, TableColumn> getColumns() {
return columns;
}
public Enumeration<String> getColumnNames() {
return columns.keys();
}
public String getFullColumnNames(boolean onlyUsed) {
String ret_val = "";
int count = 0;
for (TableColumn column : columns.values()) {
if ((column.isUsed() && onlyUsed) || (!onlyUsed)) {
if (count > 0) {
ret_val += ", ";
}
ret_val += tableName + "." + column.name;
count++;
}
}
return ret_val;
}
public String getTableName() {
return tableName;
}
/**
* This function adds a new column to the stored list of columns that an
* implementation of this abstract class needs for its uses. This function is
* final because it interacts with a member of this abstract class, neither of
* which should be overridden.
*
* @param name
* @param newColumn
*/
public final void addDataColumn(String name, TableColumn newColumn) {
if ((null != columns) && (null != name) && (!name.isEmpty()) && (!columns.containsKey(name)) && (null != newColumn)) {
columns.put(name, newColumn);
}
}
/**
* This function allows access to the underlying columns' data class.
*
* @param name The string value of the name that you want access to.
* @return Returns the TableColumn matching the name parameter or throws an
* exception when not found.
* @throws ServiceException
*
* @see TableColumn
* @see TableColumnBoolean
* @see TableColumnDouble
* @see TableColumnInteger
* @see TableColumnString
*
*/
public TableColumn get(String name) throws ServiceException {
TableColumn ret_val = null;
if ((null != name) && (columns.containsKey(name))) {
ret_val = columns.get(name);
} else {
throw new ServiceException("Column name, " + name + ", not found in this database_table instance.");
}
return ret_val;
}
public ArrayList<String> getColumnList() {
ArrayList<String> ret_val = new ArrayList<>();
for (String key : columns.keySet()) {
ret_val.add(key);
}
return ret_val;
}
/**
*
* @param dontUseThis
*/
public void setDontUseColumn(String dontUseThis) {
if (null != dontUseThis) {
TableColumn column = columns.get(dontUseThis);
if ((null != column) && ((null != getMandatoryColumns()) && (!getMandatoryColumns().contains(dontUseThis)))) {
column.setUsed(false);
}
}
}
/**
*
* @param dontUseThis
*/
public void setNonOutputColumn(String dontUseThis) {
if (null != dontUseThis) {
TableColumn column = columns.get(dontUseThis);
if (null != column) {
column.setIncludeInOutput(false);
}
}
}
/**
* This function sets the columns of this table to not be included in output
* of JSON arrays, if those columns are contained in the parameter
* dontUseList. The side-effect of this function is that all columns not
* listed in the parameter dontUseList are set to be used in the output.
*
* @param dontUseList
*/
public void setNonOutputColumns(List<String> dontUseList) {
for (String key : columns.keySet()) {
if ((null != dontUseList) && dontUseList.contains(key)) {
columns.get(key).setIncludeInOutput(false);
} else {
columns.get(key).setIncludeInOutput(true);
}
}
}
/**
*
* @param useThis
*/
public void setOutputColumn(String useThis) {
if ((null != useThis) && (!useThis.isEmpty())) {
TableColumn column = columns.get(useThis);
if (null != column) {
column.setIncludeInOutput(true);
}
}
}
/**
*
* @param orderedList
*/
public void setOutputColumnOrdering(List<String> orderedList) {
if (null != orderedList) {
if (outputOrderList.size() > 0) {
outputOrderList.clear();
}
for (String key : orderedList) {
outputOrderList.add(key);
}
setOutputColumns(orderedList);
}
}
/**
*
* @param usedList
*/
public void setOutputColumns(List<String> usedList) {
if (null != usedList) {
for (String key : columns.keySet()) {
if ((null != usedList) && usedList.contains(key)) {
columns.get(key).setIncludeInOutput(true);
} else {
columns.get(key).setIncludeInOutput(false);
}
}
}
}
/**
* This function is used to set the columns that are required when
* <b>reading</b>
* JSON input only. It has no effect on columns used for SQL statements or for
* output to JSON. It has the side-effect of setting all table columns that
* are not included in the parameter requiredList to be <b>not required</b>
* for reading input JSON.
*
* @param requiredList
*/
public void setRequiredColumns(List<String> requiredList) {
if (null != requiredList) {
for (String key : columns.keySet()) {
if ((null != requiredList) && requiredList.contains(key)) {
columns.get(key).setRequired(true);
} else {
columns.get(key).setRequired(false);
}
}
}
}
/**
*
* @param dontUseList
*/
public void setUnusedColumns(List<String> dontUseList) {
for (String key : columns.keySet()) {
if ((null != dontUseList) && dontUseList.contains(key)) {
columns.get(key).setUsed(false);
} else {
columns.get(key).setUsed(true);
}
}
if (null != getMandatoryColumns()) {
for (String key : getMandatoryColumns()) {
if (columns.contains(key)) {
columns.get(key).setUsed(true);
}
}
}
}
/**
*
* @param useThis
*/
public void setUseColumn(String useThis) {
if ((null != useThis) && (!useThis.isEmpty())) {
TableColumn column = columns.get(useThis);
if (null != column) {
column.setUsed(true);
}
}
}
/**
*
* @param usedList
*/
public void setUsedColumns(List<String> usedList) {
for (String key : columns.keySet()) {
if ((null != usedList) && usedList.contains(key)) {
columns.get(key).setUsed(true);
} else {
columns.get(key).setUsed(false);
}
}
if (null != getMandatoryColumns()) {
for (String key : getMandatoryColumns()) {
if (columns.contains(key)) {
columns.get(key).setUsed(true);
}
}
}
}
/**
*
* @param table
* @throws ServiceException
*/
public void merge(Table table) throws ServiceException {
if (null == table) {
throw new ServiceException("NULL Table specified for merge operation. Cannot continue.");
}
for (String key : columns.keySet()) {
if (table.get(key).wasSetInJSON()) {
columns.get(key).setValue(table.get(key).getValue());
columns.get(key).wasSetInJSON(true);
}
}
}
/**
*
* @param dataJSON
* @throws ServiceException
* @throws JSONException
*/
public final void readValuesFromJSON(JSONArray dataJSON) throws ServiceException, JSONException {
readValuesFromJSON(preprocess(dataJSON));
}
/**
*
* @param tableArray
* @throws ServiceException
* @throws JSONException
*/
public final void readValuesFromJSON(Map<String, JSONObject> tableArray) throws ServiceException, JSONException {
if (null == tableArray) {
throw new ServiceException("NULL JSON Object Array value was passed to readValuesFromJSON. Cannot continue.");
}
if (tableArray.size() > 0) {
for (String key : columns.keySet()) {
if ((columns.get(key).isUsed()) && (tableArray.containsKey(key))) {
columns.get(key).valueFromJSON(tableArray.get(key));
} else {
if ((columns.get(key).isRequired()) && (!tableArray.containsKey(key))) {
throw new ServiceException("Required variable, " + key + ", was not found in the input JSON.");
}
}
}
} else {
throw new ServiceException("An empty JSON Object Array value was passed to readValuesFromJSON. Cannot continue; no JSON to read.");
}
}
/**
* This function reads the columns found in the ResultSet passed to it into
* the Table. If the ResultSet contains columns that were not originally set
* as "Used" in the column object, those matching columns will be changed to
* "Used" and the values will be read anyway. The assumption here is that the
* governing SQL statement executed contains columns for this Table on purpose
* and therefore, if they were not marked as "Used", i.e. mandatory in reading
* the ResultSet, then this was a mistake in the calling code for some reason.
* Ultimately, it is also assumed that reading these values will have no
* harmful effects on the code using these table functions or else the code
* would not have specified table columns in the SQL statement that would have
* reset or otherwise overwritten existing values in the table.
*
* @param results
* @throws ServiceException
*/
public void readValuesFromSQL(ResultSet results) throws ServiceException {
for (String key : columns.keySet()) {
if (columns.get(key).isUsed()) {
columns.get(key).valueFromSQL(results);
}
}
// for (String key : columns.keySet()) {
// boolean hasField = true;
// tr5y {
// results.findColumn(key);
// } catch (SQLException ex) {
// hasField = false;
// }
// if (hasField) {
// columns.get(key).valueFromSQL(results);
// }
// }
// if (null != results) {
// ResultSetMetaData rsMD;
// int count = 0;
// try {
// rsMD = results.getMetaData();
//
// } catch (SQLException ex) {
// throw new ServiceException("Cannot get ResultSetMetaData from the results passed to Table::readValuesFromSQL: " + ex.getMessage(), ex);
// }
//
// try {
// count = rsMD.getColumnCount();
// } catch (SQLException ex) {
// throw new ServiceException("Cannot get ResultSetMetaData ColumnCount from the results passed to Table::readValuesFromSQL: " + ex.getMessage(), ex);
// }
//
// for (int i = 1; i <= count; i++) {
// TableColumn tColumn = null;
// try {
// String name = rsMD.getColumnName(i);
// if (null != name) {
// tColumn = columns.get(name);
// }
// else{
// System.out.println("found a null column name in the returned ResultSet...");
// }
// } catch (SQLException ex) {
// throw new ServiceException("Cannot get ColumnName from ResultSetMetaData for index: " + i + ", : " + ex.getMessage(), ex);
// }
// if (null != tColumn) {
// tColumn.setUsed(true);
// tColumn.valueFromSQL(results);
// }
// }
// } else {
// throw new ServiceException("NULL ResultSet passed to Table::readValuesFromSQL");
// }
}
/**
*
* @param outArray
* @throws JSONException
*/
public synchronized void toJSON(JSONArray outArray) throws JSONException {
if (outputOrderList.size() > 0) {
for (String key : outputOrderList) {
TableColumn column = columns.get(key);
if ((null != column) && column.includeInOutput()) {
outArray.put(columns.get(key).toJSON());
}
}
} else {
for (String key : columns.keySet()) {
TableColumn column = columns.get(key);
if ((null != column) && column.includeInOutput()) {
outArray.put(columns.get(key).toJSON());
}
}
}
}
/**
*
* @param outArray
* @throws JSONException
*/
public synchronized void toBasicJSON(JSONObject outObject) throws JSONException {
if (outputOrderList.size() > 0) {
for (String key : outputOrderList) {
TableColumn column = columns.get(key);
if ((null != column) && column.includeInOutput()) {
outObject.put(key, columns.get(key).toWriteString());
}
}
} else {
for (String key : columns.keySet()) {
TableColumn column = columns.get(key);
if ((null != column) && column.includeInOutput()) {
outObject.put(key, columns.get(key).toWriteString());
}
}
}
}
public boolean isEqual(Table table) throws ServiceException {
if (!this.tableName.getClass().getName().equals(table.getClass().getName())) {
return false;
}
ArrayList<String> tableKeys = table.getColumnList();
if (columns.size() != tableKeys.size()) {
return false;
}
for (String key : columns.keySet()) {
if (!tableKeys.contains(key) || !columns.get(key).isEqual(table.get(key).getValue())) {
return false;
}
}
return true;
}
}