package hirondelle.stocks.table;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import java.beans.*;
import hirondelle.stocks.quotes.Quote;
import hirondelle.stocks.util.Args;
import hirondelle.stocks.util.ui.UiUtil;
import hirondelle.stocks.preferences.QuoteTablePreferencesEditor;
public final class QuoteTable extends JScrollPane
implements PropertyChangeListener, Observer {
public QuoteTable(
QuoteTablePreferencesEditor aTablePrefsEditor,
QuoteFilterFactory aQuoteFilterFactory
){
fTablePrefsEditor = aTablePrefsEditor;
fTablePrefsEditor.addObserver(this);
fQuoteFilterFactory = aQuoteFilterFactory;
fQuoteFilterFactory.addPropertyChangeListener(this);
fModel = new QuoteTableModel();
fTable = new JTable(fModel);
fTableSortIndicator = new TableSortIndicator(fTable, UP_ICON, DOWN_ICON);
fTableSortIndicator.addObserver(this); initGui();
}
public void setQuoteTable(List<Quote> aQuotes) {
fModel.setQuoteTable(aQuotes);
}
@Override public void update(Observable aObservable, Object aData) {
if ( aObservable == fTablePrefsEditor ) {
synchWithPrefs();
}
else if ( aObservable == fTableSortIndicator ) {
fModel.filterAndSortQuotes();
}
else {
throw new AssertionError("Unknown observable: " + aObservable);
}
}
@Override public void propertyChange(PropertyChangeEvent event) {
fModel.filterAndSortQuotes();
}
private QuoteTablePreferencesEditor fTablePrefsEditor;
private QuoteFilterFactory fQuoteFilterFactory;
private JTable fTable;
private QuoteTableModel fModel;
private TableSortIndicator fTableSortIndicator;
private static final ImageIcon UP_ICON =
UiUtil.getImageIcon("/toolbarButtonGraphics/navigation/Up")
;
private static final ImageIcon DOWN_ICON =
UiUtil.getImageIcon("/toolbarButtonGraphics/navigation/Down")
;
private void initGui(){
fTable.getTableHeader().setReorderingAllowed(false);
fTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
synchWithPrefs();
setViewportView( fTable );
}
private void synchWithPrefs(){
fTable.setShowHorizontalLines( fTablePrefsEditor.hasHorizontalLines() );
fTable.setShowVerticalLines ( fTablePrefsEditor.hasVerticalLines() );
fTable.setRowHeight( fTablePrefsEditor.getRowHeight() );
fModel.setColumnMapping( new ArrayList(fTablePrefsEditor.getColumnOrder()) );
synchWithTableRendererPrefs();
int sortIdx = fModel.fColumnMapping.indexOf( fTablePrefsEditor.getSortField() );
fTableSortIndicator.setSortBy( new SortBy(SortOrder.DESCENDING, sortIdx) ) ;
}
private void synchWithTableRendererPrefs(){
for (int idx = 0; idx < fTable.getColumnCount(); ++idx) {
String colName = fTable.getColumnName(idx);
QuoteField field = QuoteField.valueFrom(colName);
if (
field == QuoteField.Change ||
field == QuoteField.PercentChange ||
field == QuoteField.Profit ||
field == QuoteField.PercentProfit
) {
fTable.getColumnModel().getColumn(idx).setCellRenderer(new RenderRedGreen());
}
else if ( field == QuoteField.Price ) {
fTable.getColumnModel().getColumn(idx).setCellRenderer(new RenderPrice());
}
else if ( field == QuoteField.Stock ){
fTable.getColumnModel().getColumn(idx).setCellRenderer(new RenderStockName());
}
}
}
private final class QuoteTableModel extends AbstractTableModel {
QuoteTableModel(){
setColumnMapping(new ArrayList(fTablePrefsEditor.getColumnOrder()));
fQuoteTable = Collections.emptyList();
fFilteredSortedQuoteTable = Collections.emptyList();
}
void setColumnMapping(List<QuoteField> aColumnMapping ){
if (aColumnMapping == null || aColumnMapping.size() != QuoteField.values().length ){
throw new IllegalArgumentException( "Column mapping null or of unexpected size.");
}
fColumnMapping = Collections.unmodifiableList(aColumnMapping);
fireTableStructureChanged();
}
void setQuoteTable( List<Quote> aQuoteTable ){
Args.checkForNull(aQuoteTable);
fQuoteTable = Collections.unmodifiableList(aQuoteTable);
filterAndSortQuotes();
}
@Override public String getColumnName(int aColumnIdx) {
return getField(aColumnIdx).toString();
}
@Override public Class<?> getColumnClass(int aColumnIdx) {
Object value = getValueAt(0, aColumnIdx);
return value.getClass();
}
@Override public int getColumnCount() {
return fColumnMapping.size();
}
@Override public int getRowCount() {
return fFilteredSortedQuoteTable.size();
}
@Override public Object getValueAt(int aRowIdx, int aColumnIdx) {
if (aRowIdx < 0 || aRowIdx > getRowCount()-1) {
throw new IllegalArgumentException("Row index is out of range: " + aRowIdx);
}
if (aColumnIdx < 0 || aColumnIdx > getColumnCount()-1) {
throw new IllegalArgumentException("Column index is out of range: " + aColumnIdx);
}
Quote quote = (Quote)fFilteredSortedQuoteTable.get(aRowIdx);
QuoteField field = getField(aColumnIdx);
return getFieldValue( quote, field );
}
private List<Quote> fQuoteTable;
private List<Quote> fFilteredSortedQuoteTable;
private List<QuoteField> fColumnMapping;
private void filterAndSortQuotes(){
fFilteredSortedQuoteTable = fQuoteFilterFactory.getSelectedFilter().sift(fQuoteTable);
Collections.sort(fFilteredSortedQuoteTable, getQuoteSorter());
if (fTableSortIndicator.getSortBy().getOrder() == SortOrder.ASCENDING) {
Collections.reverse(fFilteredSortedQuoteTable);
}
fireTableDataChanged();
}
private Comparator<Quote> getQuoteSorter(){
SortBy sortBy = fTableSortIndicator.getSortBy();
QuoteField quoteField = fModel.getField( sortBy.getColumn() );
return QuoteSorterFactory.getSorter( quoteField );
}
private QuoteField getField( int aColumnIdx ){
return (QuoteField)fColumnMapping.get(aColumnIdx);
}
private Object getFieldValue(Quote aQuote, QuoteField aField) {
Object result = null;
if ( aField == QuoteField.Stock ){
result = aQuote.getStock();
}
else if (aField == QuoteField.Price) {
result = aQuote.getPrice();
}
else if (aField == QuoteField.Change) {
result = aQuote.getChange();
}
else if (aField == QuoteField.PercentChange) {
result = aQuote.getPercentChange();
}
else if (aField == QuoteField.Profit) {
result = aQuote.getProfit();
}
else if (aField == QuoteField.PercentProfit) {
result = aQuote.getPercentProfit();
}
else {
throw new AssertionError("Unknown field: " + aField);
}
return result;
}
}
}