package hirondelle.stocks.portfolio;
import hirondelle.stocks.quotes.Exchange;
import hirondelle.stocks.quotes.Stock;
import hirondelle.stocks.util.Consts;
import hirondelle.stocks.util.Util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.InvalidPreferencesFormatException;
import java.util.prefs.Preferences;
public final class PortfolioDAO {
public void saveAs(Portfolio aPortfolio){
if( isStored(aPortfolio.getName()) ){
String message = "Cannot save; name already exists: " + aPortfolio.getName();
IllegalArgumentException ex = new IllegalArgumentException(message);
throw ex;
}
getPortfoliosRootPref().node( aPortfolio.getName() );
save(aPortfolio);
}
public void save(Portfolio aPortfolio){
Preferences portfolioPref = getExistingPortfolioPref(aPortfolio.getName());
portfolioPref.put(STOCKS_KEY, aPortfolio.getStocks().toString() );
}
public void saveAsDefault( Portfolio aPortfolio ){
Preferences rootPref = getPortfoliosRootPref();
rootPref.put(DEFAULT_PORTFOLIO_NAME_KEY, aPortfolio.getName());
}
public Portfolio fetchDefaultPortfolio(){
Preferences rootPref = getPortfoliosRootPref();
String defaultPortfolioName = rootPref.get(
DEFAULT_PORTFOLIO_NAME_KEY, Consts.EMPTY_STRING
);
if ( Util.textHasContent(defaultPortfolioName) ) {
return fetch ( defaultPortfolioName );
}
else {
return Portfolio.getUntitledPortfolio();
}
}
public Collection<String> fetchAllPortfolioNames(){
String[] names = null;
try {
names = getPortfoliosRootPref().childrenNames();
}
catch (BackingStoreException ex) {
fLogger.log(Level.SEVERE, "Cannot access backing store.", ex);
}
return new TreeSet<String>( Arrays.asList(names) );
}
public boolean isValidCandidateName(String aNewName){
Collection<String> allNames = fetchAllPortfolioNames();
return ( Util.textHasContent(aNewName) && !allNames.contains(aNewName) );
}
public Portfolio fetch(String aPortfolioName){
Preferences portfolioPref = getExistingPortfolioPref(aPortfolioName);
return new Portfolio(aPortfolioName, getStocks(portfolioPref) );
}
public void delete( Portfolio aPortfolio ){
String defaultPortfolioName = fetchDefaultPortfolio().getName();
if ( aPortfolio.getName().equals(defaultPortfolioName) ) {
getPortfoliosRootPref().remove(DEFAULT_PORTFOLIO_NAME_KEY);
}
Preferences portfolioPref = getExistingPortfolioPref(aPortfolio.getName());
try {
portfolioPref.removeNode();
}
catch (BackingStoreException ex) {
fLogger.log(Level.SEVERE, "Cannot access backing store", ex);
}
}
public void deleteAll(){
try {
getPortfoliosRootPref().removeNode();
}
catch ( BackingStoreException ex ) {
fLogger.log(Level.SEVERE, "Cannot access backing store.", ex);
}
}
public void exportXML(File aFile) {
try {
OutputStream output = null;
try {
output = new BufferedOutputStream( new FileOutputStream( aFile ) );
getPortfoliosRootPref().exportSubtree(output);
}
finally {
output.close();
}
}
catch (IOException ex) {
fLogger.severe("Cannot save to file " + aFile);
}
catch(BackingStoreException ex){
fLogger.log(Level.SEVERE, "Cannot access backing store.", ex);
}
}
public void importXML(File aFile) {
try {
InputStream input = null;
try {
input = new BufferedInputStream( new FileInputStream( aFile ) );
getPortfoliosRootPref().importPreferences(input);
}
finally {
input.close();
}
}
catch (IOException ex) {
fLogger.severe("Cannot read file " + aFile);
}
catch(InvalidPreferencesFormatException ex){
fLogger.log(Level.SEVERE, "Format of Preferences file is invalid.", ex);
}
}
private static final Logger fLogger = Util.getLogger(PortfolioDAO.class);
private static final String STOCKS_KEY = "stocks";
private static final String DEFAULT_PORTFOLIO_NAME_KEY = "DefaultPortfolioName";
private static Preferences getPortfoliosRootPref(){
return Preferences.systemNodeForPackage(PortfolioDAO.class);
}
private Preferences getExistingPortfolioPref( String aPortfolioName ){
if ( isStored(aPortfolioName) ) {
return getPortfoliosRootPref().node( aPortfolioName );
}
else {
throw new IllegalArgumentException("Unknown Portfolio Name:" + aPortfolioName);
}
}
private boolean isStored(String aPortfolioName) {
boolean result = false;
try {
result = getPortfoliosRootPref().nodeExists(aPortfolioName);
}
catch ( BackingStoreException ex){
fLogger.severe(
"Cannot access backing store for Portfolio Name: " + aPortfolioName +
" Exception: " + ex
);
}
return result;
}
private Set<Stock> getStocks( Preferences aPortfolioPref ){
Collection<Stock> result = new HashSet<Stock>();
String rawStocks = aPortfolioPref.get(STOCKS_KEY, Consts.EMPTY_STRING);
String delimiters = "[],";
StringTokenizer parser = new StringTokenizer( rawStocks, delimiters );
while ( parser.hasMoreTokens() ) {
String rawStock = parser.nextToken().trim();
if ( rawStockHasContent(rawStock) ) {
Stock stock = getStock(rawStock);
result.add(stock);
}
}
return new TreeSet<Stock>(result);
}
private boolean rawStockHasContent( String aRawStock ){
return
aRawStock!=null &&
aRawStock.trim().length()>0 &&
!aRawStock.equalsIgnoreCase("null") ;
}
private Stock getStock(String aRawStock){
Stock result = null;
String delimiter = ":";
StringTokenizer parser = new StringTokenizer(aRawStock, delimiter);
try {
String ticker = parser.nextToken();
String name = parser.nextToken();
Exchange exchange = Exchange.valueFrom( parser.nextToken() );
Integer quantity = Integer.valueOf( parser.nextToken() );
BigDecimal avgPrice = new BigDecimal( parser.nextToken() );
result = new Stock(name, ticker, exchange, quantity, avgPrice);
}
catch ( NoSuchElementException ex){
fLogger.severe("Cannot parse into Stock object: \"" + aRawStock + "\"");
}
return result;
}
private static void main(String... args) {
Exchange nYSEStockExchanges = Exchange.valueFrom("NYSE Stock Exchanges");
Exchange nasdaqStockExchange = Exchange.valueFrom("Nasdaq Stock Exchange");
Exchange torontoStockExchange = Exchange.valueFrom("Toronto Stock Exchange");
Stock ibm = new Stock(
"Big Blue", "IBM", nYSEStockExchanges, new Integer(100), new BigDecimal("6.00")
);
Stock csco = new Stock(
"Cisco Systems", "CSCO", nasdaqStockExchange,
new Integer(200), new BigDecimal("10.25")
);
Stock ctr = new Stock(
"Canadian Tire", "CTR", torontoStockExchange,
new Integer(100), new BigDecimal("5.00")
);
Stock mo = new Stock(
"Philip Morris", "MO", nYSEStockExchanges,
new Integer(200), new BigDecimal("3.25")
);
Set<Stock> stocks = new TreeSet<Stock>();
stocks.add(ibm);
stocks.add(csco) ;
stocks.add(ctr);
stocks.add(mo) ;
Portfolio alaska = new Portfolio("Alaska", stocks);
PortfolioDAO dao = new PortfolioDAO();
dao.saveAs( alaska );
Stock pepsi = new Stock(
"Pepsi", "PEP", nYSEStockExchanges,
new Integer(800), new BigDecimal("5.65")
);
stocks.add(pepsi);
dao.saveAs(new Portfolio("Le Havre", stocks));
dao.saveAsDefault(alaska);
}
}