org.yccheok.jstock.charting.Utils.java Source code

Java tutorial

Introduction

Here is the source code for org.yccheok.jstock.charting.Utils.java

Source

/*
 * JStock - Free Stock Market Software
 * Copyright (C) 2010 Yan Cheng CHEOK <yccheok@yahoo.com>
 *
 * 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 2 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package org.yccheok.jstock.charting;

import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.yccheok.jstock.engine.Stock;
import org.yccheok.jstock.engine.StockHistoryServer;
import org.yccheok.jstock.gui.JStockOptions;
import org.yccheok.jstock.gui.JStock;

/**
 *
 * @author yccheok
 */
public class Utils {

    // http://stackoverflow.com/questions/470690/how-to-automatically-generate-n-distinct-colors
    private static final List<Color> _kellysMaxContrastSet = new ArrayList<Color>();
    private static final List<Color> _boyntonOptimized = new ArrayList<Color>();
    static {
        // _kellysMaxContrastSet.add(new Color(0xffffff));
        _kellysMaxContrastSet.add(new Color(0x000000));
        _kellysMaxContrastSet.add(new Color(0xFFB300)); //Vivid Yellow
        _kellysMaxContrastSet.add(new Color(0x803E75)); //Strong Purple
        _kellysMaxContrastSet.add(new Color(0xFF6800)); //Vivid Orange
        _kellysMaxContrastSet.add(new Color(0xA6BDD7)); //Very Light Blue
        _kellysMaxContrastSet.add(new Color(0xC10020)); //Vivid Red
        _kellysMaxContrastSet.add(new Color(0xCEA262)); //Grayish Yellow
        _kellysMaxContrastSet.add(new Color(0x817066)); //Medium Gray

        //The following will not be good for people with defective color vision
        _kellysMaxContrastSet.add(new Color(0x007D34)); //Vivid Green
        _kellysMaxContrastSet.add(new Color(0xF6768E)); //Strong Purplish Pink
        _kellysMaxContrastSet.add(new Color(0x00538A)); //Strong Blue
        _kellysMaxContrastSet.add(new Color(0xFF7A5C)); //Strong Yellowish Pink
        _kellysMaxContrastSet.add(new Color(0x53377A)); //Strong Violet
        _kellysMaxContrastSet.add(new Color(0xFF8E00)); //Vivid Orange Yellow
        _kellysMaxContrastSet.add(new Color(0xB32851)); //Strong Purplish Red
        _kellysMaxContrastSet.add(new Color(0xF4C800)); //Vivid Greenish Yellow
        _kellysMaxContrastSet.add(new Color(0x7F180D)); //Strong Reddish Brown
        _kellysMaxContrastSet.add(new Color(0x93AA00)); //Vivid Yellowish Green
        _kellysMaxContrastSet.add(new Color(0x593315)); //Deep Yellowish Brown
        _kellysMaxContrastSet.add(new Color(0xF13A13)); //Vivid Reddish Orange
        _kellysMaxContrastSet.add(new Color(0x232C16)); //Dark Olive Green           

        _boyntonOptimized.add(new Color(0, 0, 255)); //Blue
        _boyntonOptimized.add(new Color(255, 0, 0)); //Red
        _boyntonOptimized.add(new Color(0, 255, 0)); //Green
        _boyntonOptimized.add(new Color(255, 255, 0)); //Yellow
        _boyntonOptimized.add(new Color(255, 0, 255)); //Magenta
        _boyntonOptimized.add(new Color(255, 128, 128));//Pink
        _boyntonOptimized.add(new Color(128, 128, 128));//Gray
        _boyntonOptimized.add(new Color(128, 0, 0)); //Brown
        _boyntonOptimized.add(new Color(255, 128, 0)); //Orange        
    };

    public static void setPriceSeriesPaint(XYItemRenderer xyItemRenderer) {
        //xyItemRenderer.setSeriesPaint(0, new Color(255, 85, 85));
        //for (int i = 0, ei = _kellysMaxContrastSet.size(); i < ei; i++) {
        //    xyItemRenderer.setSeriesPaint(i + 1, _kellysMaxContrastSet.get(i));
        //}
        //for (int i = 0, ei = _boyntonOptimized.size(); i < ei; i++) {
        //    xyItemRenderer.setSeriesPaint(i + 1 + _kellysMaxContrastSet.size(), _boyntonOptimized.get(i));
        //}        
    }

    public static void setVolumeSeriesPaint(XYItemRenderer xyItemRenderer) {
        //xyItemRenderer.setSeriesPaint(0, new Color(85, 85, 255));        
        //xyItemRenderer.setSeriesPaint(1, new Color(85, 85, 255));
        //xyItemRenderer.setSeriesPaint(2, new Color(85, 85, 255));
        //xyItemRenderer.setSeriesPaint(3, new Color(85, 85, 255));
    }

    /**
     * Returns daily chart data based on given stock history server.
     *
     * @param stockHistoryServer the stock history server
     * @return list of daily chart data
     */
    public static List<ChartData> getDailyChartData(StockHistoryServer stockHistoryServer) {
        final int days = stockHistoryServer.size();

        List<ChartData> chartDatas = new ArrayList<ChartData>();

        double prevPrice = 0;
        double openPrice = 0;
        double lastPrice = 0;
        double highPrice = Double.MIN_VALUE;
        double lowPrice = Double.MAX_VALUE;
        long volume = 0;
        long timestamp = 0;

        // Just perform simple one to one copy, without performing any
        // filtering.
        for (int i = 0; i < days; i++) {
            final long t = stockHistoryServer.getTimestamp(i);
            Stock stock = stockHistoryServer.getStock(t);
            prevPrice = stock.getPrevPrice();
            openPrice = stock.getOpenPrice();
            lastPrice = stock.getLastPrice();
            highPrice = stock.getHighPrice();
            lowPrice = stock.getLowPrice();
            volume = stock.getVolume();
            timestamp = stock.getTimestamp();
            ChartData chartData = ChartData.newInstance(prevPrice, openPrice, lastPrice, highPrice, lowPrice,
                    volume, timestamp);
            chartDatas.add(chartData);
        }
        return chartDatas;
    }

    /**
     * Returns weekly chart data based on given stock history server.
     * 
     * @param stockHistoryServer the stock history server
     * @return list of weekly chart data
     */
    public static List<ChartData> getWeeklyChartData(StockHistoryServer stockHistoryServer) {
        final int days = stockHistoryServer.size();
        Calendar prevCalendar = null;

        List<ChartData> chartDatas = new ArrayList<ChartData>();

        double prevPrice = 0;
        double openPrice = 0;
        double lastPrice = 0;
        double highPrice = Double.MIN_VALUE;
        double lowPrice = Double.MAX_VALUE;
        long volume = 0;
        long timestamp = 0;
        int count = 0;

        for (int i = 0; i < days; i++) {
            // First, determine the current data is same week as the previous
            // data.
            boolean isSameWeek = false;
            final long t = stockHistoryServer.getTimestamp(i);
            Stock stock = stockHistoryServer.getStock(t);

            final Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(t);

            if (prevCalendar != null) {
                int weekOfYear = calendar.get(Calendar.WEEK_OF_YEAR);
                int prevWeekOfYear = prevCalendar.get(Calendar.WEEK_OF_YEAR);
                // Is this the same week?
                isSameWeek = (weekOfYear == prevWeekOfYear);
            } else {
                // First time for us to enter this for loop.
                isSameWeek = true;
                openPrice = stock.getOpenPrice();
                prevPrice = stock.getPrevPrice();
            }

            if (isSameWeek == false) {
                // This is a new week. There must be data for previous week.
                assert (count > 0);
                ChartData chartData = ChartData.newInstance(prevPrice, openPrice, lastPrice / count, // Average last price.
                        highPrice, lowPrice, volume / count, // Average volume.
                        timestamp);
                chartDatas.add(chartData);

                // First day of the week.
                prevPrice = stock.getPrevPrice();
                openPrice = stock.getOpenPrice();
                lastPrice = stock.getLastPrice();
                highPrice = stock.getHighPrice();
                lowPrice = stock.getLowPrice();
                volume = stock.getVolume();
                timestamp = stock.getTimestamp();
                count = 1;
            } else {
                // We will not update prevPrice and openPrice. They will remain
                // as the first day of the week's.
                lastPrice += stock.getLastPrice();
                highPrice = Math.max(highPrice, stock.getHighPrice());
                lowPrice = Math.min(lowPrice, stock.getLowPrice());
                volume += stock.getVolume();
                timestamp = stock.getTimestamp();
                count++;
            }

            prevCalendar = calendar;
        }

        // Is there any data which is not being inserted yet?
        if (count > 0) {
            ChartData chartData = ChartData.newInstance(prevPrice, openPrice, lastPrice / count, highPrice,
                    lowPrice, volume / count, timestamp);
            chartDatas.add(chartData);
        }

        return chartDatas;
    }

    /**
     * Returns monthly chart data based on given stock history server.
     *
     * @param stockHistoryServer the stock history server
     * @return list of monthly chart data
     */
    public static List<ChartData> getMonthlyChartData(StockHistoryServer stockHistoryServer) {
        final int days = stockHistoryServer.size();
        Calendar prevCalendar = null;

        List<ChartData> chartDatas = new ArrayList<ChartData>();

        double prevPrice = 0;
        double openPrice = 0;
        double lastPrice = 0;
        double highPrice = Double.MIN_VALUE;
        double lowPrice = Double.MAX_VALUE;
        long volume = 0;
        long timestamp = 0;
        int count = 0;

        for (int i = 0; i < days; i++) {
            // First, determine the current data is same month as the previous
            // data.
            boolean isSameMonth = false;
            final long t = stockHistoryServer.getTimestamp(i);
            Stock stock = stockHistoryServer.getStock(t);

            final Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(t);

            if (prevCalendar != null) {
                int month = calendar.get(Calendar.MONTH);
                int prevMonth = prevCalendar.get(Calendar.MONTH);
                // Is this the same month?
                isSameMonth = (month == prevMonth);
            } else {
                // First time for us to enter this for loop.
                isSameMonth = true;
                openPrice = stock.getOpenPrice();
                prevPrice = stock.getPrevPrice();
            }

            if (isSameMonth == false) {
                // This is a new month. There must be data for previous month.
                assert (count > 0);
                ChartData chartData = ChartData.newInstance(prevPrice, openPrice, lastPrice / count, // Average last price.
                        highPrice, lowPrice, volume / count, // Average volume.
                        timestamp);
                chartDatas.add(chartData);

                // First day of the month.
                prevPrice = stock.getPrevPrice();
                openPrice = stock.getOpenPrice();
                lastPrice = stock.getLastPrice();
                highPrice = stock.getHighPrice();
                lowPrice = stock.getLowPrice();
                volume = stock.getVolume();
                timestamp = stock.getTimestamp();
                count = 1;
            } else {
                // We will not update prevPrice and openPrice. They will remain
                // as the first day of the month's.
                lastPrice += stock.getLastPrice();
                highPrice = Math.max(highPrice, stock.getHighPrice());
                lowPrice = Math.min(lowPrice, stock.getLowPrice());
                volume += stock.getVolume();
                timestamp = stock.getTimestamp();
                count++;
            }

            prevCalendar = calendar;
        }

        // Is there any data which is not being inserted yet?
        if (count > 0) {
            ChartData chartData = ChartData.newInstance(prevPrice, openPrice, lastPrice / count, highPrice,
                    lowPrice, volume / count, timestamp);
            chartDatas.add(chartData);
        }

        return chartDatas;
    }

    /**
     * Applying chart theme based on given JFreeChart.
     *
     * @param chart the JFreeChart
     */
    public static void applyChartThemeEx(JFreeChart chart) {
        final StandardChartTheme chartTheme;
        final JStockOptions.ChartTheme theme = JStock.instance().getJStockOptions().getChartTheme();

        if (theme == JStockOptions.ChartTheme.Light) {
            applyChartTheme(chart);
            return;
        } else {
            assert (theme == JStockOptions.ChartTheme.Dark);
            chartTheme = (StandardChartTheme) org.jfree.chart.StandardChartTheme.createDarknessTheme();
        }

        // The default font used by JFreeChart unable to render Chinese properly.
        // We need to provide font which is able to support Chinese rendering.
        final Locale defaultLocale = Locale.getDefault();
        if (org.yccheok.jstock.gui.Utils.isSimplifiedChinese(defaultLocale)
                || org.yccheok.jstock.gui.Utils.isTraditionalChinese(defaultLocale)) {
            final Font oldExtraLargeFont = chartTheme.getExtraLargeFont();
            final Font oldLargeFont = chartTheme.getLargeFont();
            final Font oldRegularFont = chartTheme.getRegularFont();
            final Font oldSmallFont = chartTheme.getSmallFont();

            final Font extraLargeFont = new Font("Sans-serif", oldExtraLargeFont.getStyle(),
                    oldExtraLargeFont.getSize());
            final Font largeFont = new Font("Sans-serif", oldLargeFont.getStyle(), oldLargeFont.getSize());
            final Font regularFont = new Font("Sans-serif", oldRegularFont.getStyle(), oldRegularFont.getSize());
            final Font smallFont = new Font("Sans-serif", oldSmallFont.getStyle(), oldSmallFont.getSize());

            chartTheme.setExtraLargeFont(extraLargeFont);
            chartTheme.setLargeFont(largeFont);
            chartTheme.setRegularFont(regularFont);
            chartTheme.setSmallFont(smallFont);
        }

        // Apply and return early.
        chartTheme.apply(chart);
    }

    /**
     * Applying chart theme based on given JFreeChart.
     *
     * @param chart the JFreeChart
     */
    public static void applyChartTheme(JFreeChart chart) {
        final StandardChartTheme chartTheme = (StandardChartTheme) org.jfree.chart.StandardChartTheme
                .createJFreeTheme();
        chartTheme.setXYBarPainter(barPainter);
        chartTheme.setShadowVisible(false);
        chartTheme.setPlotBackgroundPaint(Color.WHITE);
        chartTheme.setDomainGridlinePaint(Color.LIGHT_GRAY);
        chartTheme.setRangeGridlinePaint(Color.LIGHT_GRAY);
        chartTheme.setPlotOutlinePaint(Color.LIGHT_GRAY);

        // The default font used by JFreeChart unable to render Chinese properly.
        // We need to provide font which is able to support Chinese rendering.
        final Locale defaultLocale = Locale.getDefault();
        if (org.yccheok.jstock.gui.Utils.isSimplifiedChinese(defaultLocale)
                || org.yccheok.jstock.gui.Utils.isTraditionalChinese(defaultLocale)) {
            final Font oldExtraLargeFont = chartTheme.getExtraLargeFont();
            final Font oldLargeFont = chartTheme.getLargeFont();
            final Font oldRegularFont = chartTheme.getRegularFont();
            final Font oldSmallFont = chartTheme.getSmallFont();

            final Font extraLargeFont = new Font("Sans-serif", oldExtraLargeFont.getStyle(),
                    oldExtraLargeFont.getSize());
            final Font largeFont = new Font("Sans-serif", oldLargeFont.getStyle(), oldLargeFont.getSize());
            final Font regularFont = new Font("Sans-serif", oldRegularFont.getStyle(), oldRegularFont.getSize());
            final Font smallFont = new Font("Sans-serif", oldSmallFont.getStyle(), oldSmallFont.getSize());

            chartTheme.setExtraLargeFont(extraLargeFont);
            chartTheme.setLargeFont(largeFont);
            chartTheme.setRegularFont(regularFont);
            chartTheme.setSmallFont(smallFont);
        }

        if (chart.getPlot() instanceof CombinedDomainXYPlot) {
            @SuppressWarnings("unchecked")
            List<Plot> plots = ((CombinedDomainXYPlot) chart.getPlot()).getSubplots();
            for (Plot plot : plots) {
                final int domainAxisCount = ((XYPlot) plot).getDomainAxisCount();
                final int rangeAxisCount = ((XYPlot) plot).getRangeAxisCount();
                for (int i = 0; i < domainAxisCount; i++) {
                    ((XYPlot) plot).getDomainAxis(i).setAxisLinePaint(Color.LIGHT_GRAY);
                    ((XYPlot) plot).getDomainAxis(i).setTickMarkPaint(Color.LIGHT_GRAY);
                }
                for (int i = 0; i < rangeAxisCount; i++) {
                    ((XYPlot) plot).getRangeAxis(i).setAxisLinePaint(Color.LIGHT_GRAY);
                    ((XYPlot) plot).getRangeAxis(i).setTickMarkPaint(Color.LIGHT_GRAY);
                }
            }
        } else {
            final Plot plot = chart.getPlot();
            if (plot instanceof XYPlot) {
                final org.jfree.chart.plot.XYPlot xyPlot = (org.jfree.chart.plot.XYPlot) plot;
                final int domainAxisCount = xyPlot.getDomainAxisCount();
                final int rangeAxisCount = xyPlot.getRangeAxisCount();
                for (int i = 0; i < domainAxisCount; i++) {
                    xyPlot.getDomainAxis(i).setAxisLinePaint(Color.LIGHT_GRAY);
                    xyPlot.getDomainAxis(i).setTickMarkPaint(Color.LIGHT_GRAY);
                }
                for (int i = 0; i < rangeAxisCount; i++) {
                    xyPlot.getRangeAxis(i).setAxisLinePaint(Color.LIGHT_GRAY);
                    xyPlot.getRangeAxis(i).setTickMarkPaint(Color.LIGHT_GRAY);
                }
            }
            //else if (plot instanceof org.jfree.chart.plot.PiePlot) {
            //    final org.jfree.chart.plot.PiePlot piePlot = (org.jfree.chart.plot.PiePlot)plot;
            //    
            //}
        }

        chartTheme.apply(chart);
    }

    private static final org.jfree.chart.renderer.xy.StandardXYBarPainter barPainter = new org.jfree.chart.renderer.xy.StandardXYBarPainter();
}