package org.cirdles.topsoil.app.table;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableView;
import org.cirdles.topsoil.app.dataset.field.Field;
import org.cirdles.topsoil.app.dataset.field.NumberField;
import org.cirdles.topsoil.app.isotope.IsotopeType;
import org.cirdles.topsoil.app.plot.TopsoilPlotType;
import org.cirdles.topsoil.app.dataset.entry.TopsoilDataEntry;
import org.cirdles.topsoil.app.util.serialization.PlotInformation;
import org.cirdles.topsoil.plot.Plot;
import java.util.*;
/**
* {@code TopsoilDataTable} is the core data model for Topsoil. It contains data in the form of an
* {@link ObservableList} of {@link TopsoilDataEntry}s, as well as supplemental information about that data, such as a
* title, column names, an {@link IsotopeType} and any open {@link Plot}s that represent data in the
* {@code TopsoilDataTable}.
*
* @author Benjamin Muldrow
*
* @see TableView
* @see TopsoilDataEntry
* @see IsotopeType
* @see Plot
*/
public class TopsoilDataTable {
//***********************
// Attributes
//***********************
/**
* The table data, in the form of {@code TopsoilDataEntry}s.
*/
private ObservableList<TopsoilDataEntry> data;
/**
* A {@code List} of {@code StringProperty}s for each of the column names.
*/
private ObservableList<StringProperty> columnNameProperties;
/**
* The {@code StringProperty} for the title of the table.
*/
private StringProperty title;
/**
* An {@code ObjectProperty} containing the {@code IsotopeType} of the TopsoilTable.
*/
private ObjectProperty<IsotopeType> isotopeType;
/**
* A {@code Map} containing {@code PlotInformation} for an open plot of each type.
*/
private HashMap<TopsoilPlotType, PlotInformation> openPlots;
//***********************
// Constructors
//***********************
/**
* Constructs a TopsoilDataTable with the specified column names, IsotopeType, and data (in the form of
* TopsoilDataEntries).
*
* @param columnNames
* @param isotopeType
* @param dataEntries
*/
public TopsoilDataTable(String[] columnNames, IsotopeType isotopeType, TopsoilDataEntry... dataEntries) {
this.data = FXCollections.observableArrayList(dataEntries);
this.isotopeType = new SimpleObjectProperty<>(isotopeType);
this.columnNameProperties = FXCollections.observableArrayList(createColumnHeaderProperties(columnNames));
this.openPlots = new HashMap<>();
}
//***********************
// Methods
//***********************
public StringProperty titleProperty() {
if (title == null) {
title = new SimpleStringProperty("Untitled Table");
}
return title;
}
/**
* Returns the title of the TopsoilDataTable.
*
* @return the String title
*/
public String getTitle() {
return titleProperty().get();
}
/**
* Sets the title of the TopsoilDataTable to the specified String.
*
* @param title the new String title
*/
public void setTitle(String title) {
titleProperty().set(title);
}
public ObjectProperty<IsotopeType> isotopeTypeObjectProperty() {
if (isotopeType == null) {
isotopeType = new SimpleObjectProperty<>(IsotopeType.Generic);
}
return isotopeType;
}
/**
* Returns the <tt>IsotopeType</tt> of the current <tt>TopsoilDataTable</tt>.
*
* @return the tableView's IsotopeType
*/
public IsotopeType getIsotopeType() {
return isotopeTypeObjectProperty().get();
}
/**
* Sets the <tt>IsotopeType</tt> of the current <tt>TopsoilDataTable</tt>.
*
* @param isotopeType the new IsotopeType
*/
public void setIsotopeType(IsotopeType isotopeType) {
isotopeTypeObjectProperty().set(isotopeType);
}
/**
* Create columnNameProperties based on both isotope flavor and user input
*
* @param columnNames array of provided columnNameProperties
* @return updated array of columnNameProperties
*/
private StringProperty[] createColumnHeaderProperties(String [] columnNames) {
String[] defaultNames = new String[]{"Column1", "Column2", "Column3", "Column4", "Column5"};
String[] resultArr;
StringProperty[] result;
if (columnNames == null) {
// resultArr = isotopeType.getHeaders();
resultArr = defaultNames;
// if some column names are provided, populate
} else if (columnNames.length < defaultNames.length) {
int difference = defaultNames.length - columnNames.length;
resultArr = new String[defaultNames.length];
int numHeaders = defaultNames.length;
// Copy provided column names to resultArr.
System.arraycopy(columnNames, 0, resultArr, 0, numHeaders - difference);
// Fill in with default column names.
System.arraycopy(defaultNames, (numHeaders - difference),
resultArr, (numHeaders - difference),
(numHeaders - (numHeaders - difference)));
}
// If too many columnNameProperties are provided, only use the first X (depending on isotope flavor)
else {
resultArr = columnNames.clone();
}
result = new SimpleStringProperty[resultArr.length];
for (int i = 0; i < resultArr.length; i++) {
result[i] = new SimpleStringProperty(resultArr[i]);
}
return result;
}
/**
* Returns the data as TopsoilDataEntries.
*
* @return an ObservableList of TopsoilDataEntry
*/
public ObservableList<TopsoilDataEntry> getData() {
return data;
}
public List<Field<Number>> getFields() {
List<Field<Number>> fields = new ArrayList<>();
for (String header : getColumnNames()) {
Field<Number> field = new NumberField(header);
fields.add(field);
}
return fields;
}
/**
* Returns the column names as their associated StringProperties.
*
* @return an ObservableList of StringProperties
*/
public ObservableList<StringProperty> getColumnNameProperties() {
return columnNameProperties;
}
/** {@inheritDoc} */
public String[] getColumnNames() {
String[] result = new String[columnNameProperties.size()];
for (int i = 0; i < columnNameProperties.size(); i++) {
result[i] = columnNameProperties.get(i).get();
}
return result;
}
/**
* Returns a copy of the data as a new ObservableList of TopsoilDataEntries.
*
* @return the copied ObservableList of TopsoilDataEntries
*/
public ObservableList<TopsoilDataEntry> getCopyOfDataAsEntries() {
ObservableList<TopsoilDataEntry> newData = FXCollections.observableArrayList();
TopsoilDataEntry newEntry;
for (TopsoilDataEntry entry : data) {
newEntry = new TopsoilDataEntry();
for (DoubleProperty property : entry.getProperties()) {
newEntry.addValues(property.get());
}
newData.add(newEntry);
}
return newData;
}
/**
* Returns the data as a List of Double arrays.
*
* @return a List of Double[]
*/
public List<Double[]> getDataAsArrays() {
ArrayList<Double[]> tableEntries = new ArrayList<>();
for (TopsoilDataEntry entry : data) {
tableEntries.add(entry.toArray());
}
return tableEntries;
}
/**
* Returns true if the <tt>TableView</tt> is empty. In this case, "empty" means that it has one data entry filled
* with 0.0s.
*
* @return true or false
*/
public boolean isCleared() {
return data.isEmpty();
}
/**
* Returns a Collection of <tt>PlotInformation</tt>, each containing information on an open plot associated with
* this data tableView.
*
* @return a Collection of PlotInformation objects
*/
public Collection<PlotInformation> getOpenPlots() {
return this.openPlots.values();
}
/**
* Adds information about an open plot to this data tableView.
*
* @param plotInfo a new PlotInformation
*/
public void addOpenPlot(PlotInformation plotInfo) {
this.openPlots.put(plotInfo.getTopsoilPlotType(), plotInfo);
}
/**
* Removes information about an open plot to this data tableView (typically once the tableView has been closed).
*
* @param plotType the TopsoilPlotType of the plot to be removed
*/
public void removeOpenPlot(TopsoilPlotType plotType) {
this.openPlots.get(plotType).killPlot();
this.openPlots.remove(plotType);
}
}