package hirondelle.stocks.table;
import java.util.logging.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import java.awt.*;
import hirondelle.stocks.quotes.Stock;
import hirondelle.stocks.quotes.Quote;
import hirondelle.stocks.quotes.Exchange;
import hirondelle.stocks.util.Consts;
import hirondelle.stocks.portfolio.CurrentPortfolio;
import hirondelle.stocks.util.Util;
public final class QuoteFilterFactory extends JScrollPane implements Observer {
public QuoteFilterFactory (CurrentPortfolio aCurrentPortfolio){
fCurrentPortfolio = aCurrentPortfolio;
fCurrentPortfolio.addObserver(this);
fSelectedFilter = NO_SELECTION_FILTER;
initGui();
}
public QuoteFilter getSelectedFilter(){
assert fSelectedFilter != null : "Filter has null value.";
return fSelectedFilter;
}
@Override public void update(Observable aObservable, Object aData) {
fSelectedFilter = NO_SELECTION_FILTER;
synchFilterTreeWithCurrentPortfolio();
}
public static final String SELECTED_FILTER = "SelectedFilter";
private CurrentPortfolio fCurrentPortfolio;
private DefaultTreeModel fFilterTreeModel;
private JTree fFilterSelector;
private QuoteFilter fSelectedFilter;
private QuoteFilter NO_SELECTION_FILTER = new QuoteFilterAcceptAll(Consts.EMPTY_STRING);
private static final String FILTER_BY = "Filter By";
private static final String EXCHANGE = "Exchange";
private static final String TODAYS_CHANGE = "Today's Change";
private static final String GAINERS = "Gainers";
private static final String LOSERS = "Losers";
private static final String TICKER_TYPE = "Ticker Type";
private static final String INDEX = "Index";
private static final String NON_INDEX = "Non-Index";
private static final Logger fLogger = Util.getLogger(QuoteFilterFactory.class);
private void initGui(){
initFilterTreeModel();
initFilterTree();
synchFilterTreeWithCurrentPortfolio();
setViewportView( fFilterSelector );
setPreferredSize( new Dimension(160,240) );
}
private void initFilterTreeModel(){
DefaultMutableTreeNode exchange = new DefaultMutableTreeNode(
new QuoteFilterAcceptAll(EXCHANGE)
);
DefaultMutableTreeNode todaysChange = new DefaultMutableTreeNode(
new QuoteFilterAcceptAll(TODAYS_CHANGE)
);
todaysChange.add( new DefaultMutableTreeNode(new QuoteFilterGainers()) );
todaysChange.add( new DefaultMutableTreeNode(new QuoteFilterLosers()) );
DefaultMutableTreeNode tickerType = new DefaultMutableTreeNode(
new QuoteFilterAcceptAll(TICKER_TYPE)
);
tickerType.add( new DefaultMutableTreeNode(new QuoteFilterIndex()) );
tickerType.add( new DefaultMutableTreeNode(new QuoteFilterNonIndex()) );
DefaultMutableTreeNode filterBy = new DefaultMutableTreeNode(
new QuoteFilterAcceptAll(FILTER_BY)
);
filterBy.add(exchange);
filterBy.add(todaysChange);
filterBy.add(tickerType);
fFilterTreeModel = new DefaultTreeModel(filterBy);
}
private void initFilterTree(){
fFilterSelector = new JTree(fFilterTreeModel);
fFilterSelector.setRootVisible(true); fFilterSelector.setShowsRootHandles(true); fFilterSelector.setEditable(false);
fFilterSelector.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION
);
fFilterSelector.addTreeSelectionListener(new TreeSelectionListener() {
@Override public void valueChanged(TreeSelectionEvent e) {
updateFilterAndBroadcast();
}
});
}
private void synchFilterTreeWithCurrentPortfolio() {
DefaultMutableTreeNode filterBy =
(DefaultMutableTreeNode)fFilterTreeModel.getRoot()
;
DefaultMutableTreeNode exchangesNode =
(DefaultMutableTreeNode)filterBy.getFirstChild()
;
exchangesNode.removeAllChildren();
Set<Exchange> exchanges = new LinkedHashSet<Exchange>();
for(Stock stock: fCurrentPortfolio.getStocks()){
Exchange exchange = stock.getExchange();
if ( ! exchanges.contains(exchange) ) {
exchanges.add(exchange);
exchangesNode.add(new DefaultMutableTreeNode(new QuoteFilterExchange(exchange)));
}
}
fFilterTreeModel.reload(); }
private void updateFilterAndBroadcast() {
if ( hasNoSelection() ) return;
QuoteFilter newSelectedFilter = parseSelectedFilter();
assert newSelectedFilter != null : "New filter is unexpectedly null.";
assert newSelectedFilter != NO_SELECTION_FILTER : "New Filter is unexpected value.";
if ( fSelectedFilter == newSelectedFilter ) {
fLogger.fine("No broadcast: user selection does not have a new filter");
}
else {
fLogger.fine("New filter being broadcast: " + newSelectedFilter);
QuoteFilter oldSelectedFilter = fSelectedFilter;
fSelectedFilter = newSelectedFilter;
firePropertyChange(SELECTED_FILTER, oldSelectedFilter, fSelectedFilter);
}
}
private boolean hasNoSelection(){
return (getSelectedNode() == null);
}
private QuoteFilter parseSelectedFilter(){
assert getSelectedNode() != null : "Selected Filter unexpectedly null.";
return (QuoteFilter)getSelectedNode().getUserObject();
}
private DefaultMutableTreeNode getSelectedNode(){
return (DefaultMutableTreeNode) fFilterSelector.getLastSelectedPathComponent();
}
static private class QuoteFilterAcceptAll extends QuoteFilter {
QuoteFilterAcceptAll(String aText) {
fText = aText;
}
@Override public boolean isAcceptable( Quote aQuote ){
return true;
}
@Override public String toString(){
return fText;
}
private final String fText;
}
static private final class QuoteFilterExchange extends QuoteFilter {
QuoteFilterExchange( Exchange aExchange ) {
fTargetExchange = aExchange;
}
@Override public boolean isAcceptable( Quote aQuote ){
return aQuote.getStock().getExchange() == fTargetExchange;
}
@Override public String toString(){
return fTargetExchange.toString();
}
private Exchange fTargetExchange;
}
static private final class QuoteFilterGainers extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getChange().doubleValue() >= 0.0;
}
@Override public String toString(){
return GAINERS;
}
}
static private final class QuoteFilterLosers extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getChange().doubleValue() < 0.0;
}
@Override public String toString(){
return LOSERS;
}
}
static private final class QuoteFilterIndex extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getStock().isIndex();
}
@Override public String toString(){
return INDEX;
}
}
static private final class QuoteFilterNonIndex extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return !aQuote.getStock().isIndex();
}
@Override public String toString(){
return NON_INDEX;
}
}
}