jgnash.ui.commodity.YahooSecurityHistoryImportDialog.java Source code

Java tutorial

Introduction

Here is the source code for jgnash.ui.commodity.YahooSecurityHistoryImportDialog.java

Source

/*
 * jGnash, a personal finance application
 * Copyright (C) 2001-2012 Craig Cavanaugh
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package jgnash.ui.commodity;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import jgnash.engine.EngineFactory;
import jgnash.engine.QuoteSource;
import jgnash.engine.SecurityHistoryNode;
import jgnash.engine.SecurityNode;
import jgnash.net.security.SecurityUpdateFactory;
import jgnash.ui.components.CheckListCellRenderer;
import jgnash.ui.components.DatePanel;
import jgnash.ui.components.SortedListModel;
import jgnash.ui.util.DialogUtils;
import jgnash.ui.util.ToggleSelectionModel;
import jgnash.util.DateUtils;
import jgnash.util.Resource;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.factories.ButtonBarFactory;
import com.jgoodies.forms.layout.FormLayout;

import java.io.IOException;
import java.text.ParseException;
import java.util.logging.Level;

/**
 * Dialog that lets the user download and import security history from Yahoo
 * 
 * @author Craig Cavanaugh
 * 
 */
public class YahooSecurityHistoryImportDialog extends JDialog implements ActionListener {
    private final Resource rb = Resource.get();

    private final DatePanel startField = new DatePanel();

    private final DatePanel endField = new DatePanel();

    private final JButton okButton = new JButton(rb.getString("Button.Ok"));

    private final JButton cancelButton = new JButton(rb.getString("Button.Cancel"));

    private final JProgressBar bar = new JProgressBar();

    private final JList<SecurityNode> securityList = new JList<>();

    private final Calendar cal = Calendar.getInstance();

    private static final Pattern COMMA_DELIMITER_PATTERN = Pattern.compile(",");

    private ImportRun run;

    /**
     * Creates the dialog for importing security history from Yahoo
     */
    public YahooSecurityHistoryImportDialog() {
        super();
        setTitle(rb.getString("Title.HistoryImport"));
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        setIconImage(Resource.getImage("/jgnash/resource/gnome-money.png"));

        cal.setTime(new Date());
        cal.add(Calendar.MONDAY, -1);
        startField.setDate(cal.getTime());

        List<SecurityNode> list = EngineFactory.getEngine(EngineFactory.DEFAULT).getSecurities();

        Iterator<SecurityNode> i = list.iterator();

        while (i.hasNext()) {
            if (i.next().getQuoteSource() == QuoteSource.NONE) {
                i.remove();
            }
        }

        securityList.setModel(new SortedListModel<>(list));
        securityList.setSelectionModel(new ToggleSelectionModel());
        securityList.setCellRenderer(new CheckListCellRenderer<>(securityList.getCellRenderer()));

        layoutMainPanel();

        setMinimumSize(getSize());

        okButton.addActionListener(this);
        cancelButton.addActionListener(this);

        DialogUtils.addBoundsListener(this);
    }

    /**
     * Closes the dialog
     */
    void closeDialog() {
        dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
    }

    private void layoutMainPanel() {
        FormLayout layout = new FormLayout("r:p, $lcgap, 48dlu:g", "");
        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        builder.setDefaultDialogBorder();

        JScrollPane scrollPane = new JScrollPane(securityList);
        scrollPane.setAutoscrolls(true);

        builder.append(rb.getString("Label.StartDate"), startField);
        builder.nextLine();
        builder.appendRelatedComponentsGapRow();
        builder.nextLine();
        builder.append(rb.getString("Label.EndDate"), endField);
        builder.nextLine();
        builder.appendRelatedComponentsGapRow();
        builder.nextLine();
        builder.appendRow("f:p:g");
        builder.append(rb.getString("Label.Security"), scrollPane);
        builder.nextLine();
        builder.appendRelatedComponentsGapRow();
        builder.nextLine();
        builder.append(bar, 3);
        builder.nextLine();
        builder.appendUnrelatedComponentsGapRow();
        builder.nextLine();
        builder.append(ButtonBarFactory.buildOKCancelBar(okButton, cancelButton), 3);

        getContentPane().add(builder.getPanel());
        pack();
    }

    private void doImport() {
        bar.setIndeterminate(true);
        okButton.setEnabled(false); // do not allow another start

        Date start = startField.getDate();
        Date end = endField.getDate();

        int[] list = securityList.getSelectedIndices();
        SecurityNode[] nodes = new SecurityNode[list.length];

        for (int i = 0; i < list.length; i++) {
            nodes[i] = securityList.getModel().getElementAt(list[i]);
        }

        // create the runnable and start the thread
        run = new ImportRun(start, end, nodes);
        new Thread(run).start();
    }

    /**
     * Invoked when an action occurs.
     * 
     * @param e
     *            action event
     */
    @Override
    public void actionPerformed(final ActionEvent e) {
        if (e.getSource() == okButton) {
            doImport();
        } else if (e.getSource() == cancelButton) {
            if (run != null) {
                run.stop();
            }
            closeDialog();
        }
    }

    /**
     * This class does all the work for importing the data
     */
    private class ImportRun implements Runnable {

        private volatile Object lock = new Object();

        private Date start;

        private Date end;

        private final SecurityNode[] sNodes;

        private String a;

        private String b;

        private String c;

        private String d;

        private String e;

        private String f;

        protected ImportRun(final Date start, final Date end, final SecurityNode[] sNodes) {
            this.start = start;
            this.end = end;
            this.sNodes = sNodes;
        }

        private void parse(final SecurityNode sNode) {
            String s = sNode.getSymbol().toLowerCase();

            // http://ichart.finance.yahoo.com/table.csv?s=AMD&d=1&e=14&f=2007&g=d&a=2&b=21&c=1983&ignore=.csv << new URL 2.14.07

            StringBuilder r = new StringBuilder("http://ichart.finance.yahoo.com/table.csv?a=");
            r.append(a).append("&b=").append(b).append("&c=").append(c);
            r.append("&d=").append(d).append("&e=").append(e);
            r.append("&f=").append(f).append("&s=").append(s);
            r.append("&y=0&g=d&ignore=.csv");

            URLConnection connection = null;

            try {
                /* Yahoo uses English locale for date format... force the locale */
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);

                connection = new URL(r.toString()).openConnection();

                try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {

                    String l = in.readLine();

                    // make sure that we have valid data format.
                    if (!l.equals("Date,Open,High,Low,Close,Volume,Adj Close")) {
                        closeDialog();
                        return;
                    }

                    //Date,Open,High,Low,Close,Volume,Adj Close
                    //2007-02-13,14.75,14.86,14.47,14.60,17824500,14.60

                    l = in.readLine();
                    while (l != null && lock != null) {

                        if (!l.startsWith("<")) { // may have comments in file
                            String[] fields = COMMA_DELIMITER_PATTERN.split(l);
                            Date date = df.parse(fields[0]);
                            BigDecimal high = new BigDecimal(fields[2]);
                            BigDecimal low = new BigDecimal(fields[3]);
                            BigDecimal close = new BigDecimal(fields[4]);
                            long volume = Long.parseLong(fields[5]);

                            SecurityHistoryNode node = new SecurityHistoryNode();
                            node.setDate(date);
                            node.setPrice(close);
                            node.setVolume(volume);
                            node.setHigh(high);
                            node.setLow(low);

                            EngineFactory.getEngine(EngineFactory.DEFAULT).addSecurityHistory(sNode, node);
                        }

                        l = in.readLine();
                    }
                }

                String message = MessageFormat.format(rb.getString("Message.UpdatedPrice"), sNode.getSymbol());
                Logger.getLogger(SecurityUpdateFactory.class.getName()).info(message);

            } catch (IOException | ParseException | NumberFormatException ex) {
                Logger.getLogger(YahooSecurityHistoryImportDialog.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                if (connection != null) {
                    if (connection instanceof HttpURLConnection) {
                        ((HttpURLConnection) connection).disconnect();
                    }
                }
            }
        }

        @Override
        public void run() {
            // Mistake proof and handle reversed dates gracefully
            if (!DateUtils.before(start, end)) {
                Date t = start;
                start = end;
                end = t;
            }

            cal.setTime(start);
            a = Integer.toString(cal.get(Calendar.MONTH));
            b = Integer.toString(cal.get(Calendar.DATE));
            c = Integer.toString(cal.get(Calendar.YEAR));

            cal.setTime(end);
            d = Integer.toString(cal.get(Calendar.MONTH));
            e = Integer.toString(cal.get(Calendar.DATE));
            f = Integer.toString(cal.get(Calendar.YEAR));

            // run the import in sequence
            for (SecurityNode node : sNodes) {
                if (lock != null) { // continue?
                    parse(node);
                }
            }
            closeDialog();
        }

        protected void stop() {
            lock = null;
        }
    }
}