Package com.invient.vaadin.charts

Source Code of com.invient.vaadin.charts.InvientCharts$ChartZoomListener

/*
* Copyright 2011 Invient (www.invient.com)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.invient.vaadin.charts;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.ClientWidget;
import com.vaadin.ui.Component;

import com.invient.vaadin.charts.InvientCharts.SeriesCUR.SeriesCURType;
import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.PlotBand;
import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.PlotLine;
import com.invient.vaadin.charts.InvientChartsConfig.BaseLineConfig;
import com.invient.vaadin.charts.InvientChartsConfig.PointConfig;
import com.invient.vaadin.charts.InvientChartsConfig.SeriesConfig;
import com.invient.vaadin.charts.InvientChartsConfig.SubTitle;
import com.invient.vaadin.charts.InvientChartsConfig.Title;
import com.invient.vaadin.charts.InvientChartsConfig.XAxis;
import com.invient.vaadin.charts.InvientChartsConfig.YAxis;
import com.invient.vaadin.charts.widgetset.client.ui.VInvientCharts;

/**
* A Vaddin component representing charts. It is a the main class of
* InvientCharts library.
*
* A chart typically contains one or more series of same or different types.
* This class allows us to specify series of different types say line and pie
* and hence it makes it easy to build a combination chart.
*
* After a chart {@link InvientCharts} is created, the following changes to the
* chart will be reflected rendered on the webkit.
* <ul>
* <li>Update chart {@link Title} and/or {@link SubTitle}</li>
* <li>Modify chart size</li>
* <li>Add, update and delete of {@link PlotBand} and {@link PlotLine}</li>
* <li>Set or update axis categories</li>
* <li>Set or update axis min and max values</li>
* <li>Add, update and removal of {@link Series}</li>
* <li>Register and unregister event listeners</li>
* </ul>
*
* @author Invient
*
*/
@SuppressWarnings("serial")
@ClientWidget(VInvientCharts.class)
public class InvientCharts extends AbstractComponent {

    private InvientChartsConfig chartConfig;

    /**
     * Creates this chart object with given chart configuration
     *
     * @param chartConfig
     */
    public InvientCharts(InvientChartsConfig chartConfig) {
        if (chartConfig == null) {
            throw new IllegalArgumentException(
                    "The chart cannot be created without chartConfig argument.");
        }
        this.chartConfig = chartConfig;
    }

    /**
     * Returns chart configuration object
     *
     * @return
     */
    public InvientChartsConfig getConfig() {
        return this.chartConfig;
    }

    @Override
    public void paintContent(PaintTarget target) throws PaintException {
        super.paintContent(target);

        // Configurations options
        target.startTag("options");
        if (chartConfig != null) {
            InvientChartsUtil.writeTitleConfig(target, chartConfig.getTitle());
            InvientChartsUtil.writeSubtitleConfig(target,
                    chartConfig.getSubtitle());
            InvientChartsUtil
                    .writeCreditConfig(target, chartConfig.getCredit());
            InvientChartsUtil
                    .writeLegendConfig(target, chartConfig.getLegend());
            InvientChartsUtil.writeTooltipConfig(target,
                    chartConfig.getTooltip());
            InvientChartsUtil.writeGeneralChartConfig(target,
                    chartConfig.getGeneralChartConfig());
            InvientChartsUtil.writeSeriesConfigPerSeriesType(target,
                    chartConfig.getSeriesConfig());
            InvientChartsUtil.writeXAxes(target, chartConfig.getXAxes());
            InvientChartsUtil.writeYAxes(target, chartConfig.getYAxes());
            InvientChartsUtil.writeChartLabelConfig(target,
                    chartConfig.getChartLabel());
        }
        target.endTag("options");

        target.startTag("chartData");
        InvientChartsUtil.writeSeries(target, chartConfig
                .getGeneralChartConfig().getType(), this.chartSeries,
                chartConfig.getXAxes(), chartConfig.getYAxes());
        target.endTag("chartData");
        // Events
        target.startTag("events");
        // Chart Events
        paintChartEvents(target);
        // Series/Point Events
        paintSeriesAndPointEvents(target);
        target.endTag("events");

        // If the flag reloadChartData is true then the
        // client will ignore seriesOperations and
        // remove all existing series of a chart and
        // add series info received from the server.
        target.addAttribute("reloadChartSeries", reloadChartSeries);
        target.startTag("chartDataUpdates");
        if (!reloadChartSeries) {
            InvientChartsUtil.writeChartDataUpdates(target, seriesCURSet);
        }
        target.endTag("chartDataUpdates");
        // reset flag
        reloadChartSeries = false;
        // reset series operations
        seriesCURSet.clear();

    }

    private void paintChartEvents(PaintTarget target) throws PaintException {
        target.startTag("chartEvents");
        if (chartAddSeriesListener != null && chartAddSeriesListener.size() > 0) {
            target.addAttribute("addSeries", true);
        }
        if (chartClickListener != null && chartClickListener.size() > 0) {
            target.addAttribute("click", true);
        }
        if (chartZoomListener != null && chartZoomListener.size() > 0) {
            target.addAttribute("selection", true);
        }
        target.endTag("chartEvents");
    }

    private void paintSeriesAndPointEvents(PaintTarget target)
            throws PaintException {
        target.startTag("seriesEvents");
        // For each series type, check if listeners exist. If so then add.
        for (SeriesType seriesType : SeriesType.values()) {
            paintSeriesEvents(target, seriesType);
        }
        target.endTag("seriesEvents");
    }

    private void paintSeriesEvents(PaintTarget target, SeriesType seriesType)
            throws PaintException {
        String tagName = seriesType.getName();
        target.startTag(tagName);
        if (seriesClickListeners.containsKey(seriesType)
                && seriesClickListeners.get(seriesType).size() > 0) {
            target.addAttribute("click", true);
        }
        if (seriesHideListeners.containsKey(seriesType)
                && seriesHideListeners.get(seriesType).size() > 0) {
            target.addAttribute("hide", true);
        }
        if (seriesShowListeners.containsKey(seriesType)
                && seriesShowListeners.get(seriesType).size() > 0) {
            target.addAttribute("show", true);
        }
        if (seriesLegendItemClickListeners.containsKey(seriesType)
                && seriesLegendItemClickListeners.get(seriesType).size() > 0) {
            target.addAttribute("legendItemClick", true);
        }
        // Check for point events
        paintPointEvents(target, seriesType);
        //
        target.endTag(tagName);
    }

    private void paintPointEvents(PaintTarget target, SeriesType seriesType)
            throws PaintException {
        target.startTag("pointEvents");
        if (pointClickListeners.containsKey(seriesType)
                && pointClickListeners.get(seriesType).size() > 0) {
            target.addAttribute("click", true);
        }
        if (pointRemoveListeners.containsKey(seriesType)
                && pointRemoveListeners.get(seriesType).size() > 0) {
            target.addAttribute("remove", true);
        }
        if (pointSelectListeners.containsKey(seriesType)
                && pointSelectListeners.get(seriesType).size() > 0) {
            target.addAttribute("select", true);
        }
        if (pointUnselectListeners.containsKey(seriesType)
                && pointUnselectListeners.get(seriesType).size() > 0) {
            target.addAttribute("unselect", true);
        }
        // Event applicable only for pie chart
        if (SeriesType.PIE.equals(seriesType)
                && pieChartLegendItemClickListener.size() > 0) {
            target.addAttribute("legendItemClick", true);
        }
        target.endTag("pointEvents");
    }

    @Override
    @SuppressWarnings("unchecked")
    public void changeVariables(Object source, Map<String, Object> variables) {
        if (variables.containsKey("event")) {
            Map<String, Object> eventData = (Map<String, Object>) variables
                    .get("eventData");
            String eventName = (String) variables.get("event");
            if (eventName.equalsIgnoreCase("addSeries")) {
                fireAddSeries();
            } else if (eventName.equalsIgnoreCase("chartClick")) {
                double xAxisPos = Double.parseDouble((String) eventData
                        .get("xAxisPos"));
                double yAxisPos = Double.parseDouble((String) eventData
                        .get("yAxisPos"));
                //
                MousePosition mousePosition = getClickPosition(eventData);
                fireChartClick(new DecimalPoint(xAxisPos, yAxisPos),
                        mousePosition);
            } else if (eventName.equalsIgnoreCase("chartZoom")) {
                double xAxisMin = Double.parseDouble((String) eventData
                        .get("xAxisMin"));
                double xAxisMax = Double.parseDouble((String) eventData
                        .get("xAxisMax"));
                double yAxisMin = Double.parseDouble((String) eventData
                        .get("yAxisMin"));
                double yAxisMax = Double.parseDouble((String) eventData
                        .get("yAxisMax"));
                fireChartZoom(new ChartArea(xAxisMin, xAxisMax, yAxisMin,
                        yAxisMax));
            } else if (eventName.equalsIgnoreCase("chartResetZoom")) {
                fireChartResetZoom();
            } else if (eventName.equalsIgnoreCase("seriesClick")) {
                PointEventData pointEventData = getPointEventData(eventData);
                //
                MousePosition mousePosition = getClickPosition(eventData);
                fireSeriesClick(
                        getSeriesFromEventData(pointEventData.getSeriesName()),
                        getPointFromEventData(pointEventData), mousePosition);
            } else if (eventName.equalsIgnoreCase("seriesHide")) {
                String seriesName = (String) eventData.get("seriesName");
                fireSeriesHide(getSeriesFromEventData(seriesName));
            } else if (eventName.equalsIgnoreCase("seriesShow")) {
                String seriesName = (String) eventData.get("seriesName");
                fireSeriesShow(getSeriesFromEventData(seriesName));
            } else if (eventName.equalsIgnoreCase("seriesLegendItemClick")) {
                String seriesName = (String) eventData.get("seriesName");
                fireSeriesLegendItemClick(getSeriesFromEventData(seriesName));
            } else if (eventName.equalsIgnoreCase("pieLegendItemClick")) {
                PointEventData pointEventData = getPointEventData(eventData);
                fireLegendItemClick(getPointFromEventData(pointEventData));
            } else if (eventName.equalsIgnoreCase("pointClick")) {
                MousePosition mousePosition = getClickPosition(eventData);
                //
                PointEventData pointEventData = getPointEventData(eventData);
                firePointClick(pointEventData.getCategory(),
                        getPointFromEventData(pointEventData), mousePosition);
            } else if (eventName.equalsIgnoreCase("pointSelect")) {
                PointEventData pointEventData = getPointEventData(eventData);
                firePointSelect(pointEventData.getCategory(),
                        getPointFromEventData(pointEventData));
            } else if (eventName.equalsIgnoreCase("pointUnselect")) {
                PointEventData pointEventData = getPointEventData(eventData);
                firePointUnselect(pointEventData.getCategory(),
                        getPointFromEventData(pointEventData));
            } else if (eventName.equalsIgnoreCase("pointRemove")) {
                PointEventData pointEventData = getPointEventData(eventData);
                firePointRemove(pointEventData.getCategory(),
                        getPointFromEventData(pointEventData));
            }
        }
    }

    private Point getPointFromEventData(PointEventData eventData) {
        // First locate a series and then point
        Series series = getSeriesFromEventData(eventData.getSeriesName());
        if (series != null) {
            if (series instanceof XYSeries) {
                for (DecimalPoint point : ((XYSeries) series).getPoints()) {
                    if (point.getY() != null
                            && point.getY().doubleValue() == eventData
                                    .getPointY()
                            && point.getX() != null
                            && point.getX().doubleValue() == eventData
                                    .getPointX()) {
                        return point;
                    }
                }
            } else {
                for (DateTimePoint point : ((DateTimeSeries) series)
                        .getPoints()) {
                    if (point.getY() != null
                            && point.getY().doubleValue() == eventData
                                    .getPointY()
                            && point.getX() != null
                            && getDateInMilliseconds(point.getX(),
                                    ((DateTimeSeries) series).isIncludeTime()) == (long) eventData
                                    .getPointX()) {
                        return point;
                    }
                }
            }
        }
        // Should not happen
        // If it happens then why? Any comments???
        return null;
    }

    private static Long getDateInMilliseconds(Date dt, boolean isIncludeTime) {
        if (dt == null) {
            return null;
        }
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(dt);
        if (!isIncludeTime) {
            cal.set(Calendar.HOUR, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
        }
        return cal.getTimeInMillis();
    }

    private Series getSeriesFromEventData(String seriesName) {
        for (Series series : this.chartSeries) {
            if (series.getName().equals(seriesName)) {
                return series;
            }
        }
        // Should not happen
        // If it happens then why? Any comments???
        return null;
    }

    private void fireAddSeries() {
        fireEvent(new ChartAddSeriesEvent(this, this));
    }

    private void fireChartClick(Point point, MousePosition mousePosition) {
        fireEvent(new ChartClickEvent(this, this, point, mousePosition));
    }

    private void fireChartZoom(ChartArea selectedArea) {
        fireEvent(new ChartZoomEvent(this, this, selectedArea));
    }

    private void fireChartResetZoom() {
        fireEvent(new ChartResetZoomEvent(this, this));
    }

    private void fireSeriesClick(Series series, Point point,
            MousePosition mousePosition) {
        fireEvent(new SeriesClickEvent(this, this, series, point, mousePosition));
    }

    private void fireSeriesShow(Series series) {
        fireEvent(new SeriesShowEvent(this, this, series));
    }

    private void fireSeriesHide(Series series) {
        fireEvent(new SeriesHideEvent(this, this, series));
    }

    private void fireSeriesLegendItemClick(Series series) {
        fireEvent(new SeriesLegendItemClickEvent(this, this, series));
    }

    private void firePointClick(String category, Point point,
            MousePosition mousePosition) {
        fireEvent(new PointClickEvent(this, this, category, point,
                mousePosition));
    }

    private void firePointSelect(String category, Point point) {
        fireEvent(new PointSelectEvent(this, this, category, point));
    }

    private void firePointUnselect(String category, Point point) {
        fireEvent(new PointUnselectEvent(this, this, category, point));
    }

    private void firePointRemove(String category, Point point) {
        fireEvent(new PointRemoveEvent(this, this, category, point));
    }

    private void fireLegendItemClick(Point point) {
        fireEvent(new PieChartLegendItemClickEvent(this, this, point));
    }

    private PointEventData getPointEventData(Map<String, Object> eventData) {
        String seriesName = (String) eventData.get("seriesName");
        String category = (String) eventData.get("category");
        double pointX = Double.valueOf((String) eventData.get("pointX"));
        double pointY = Double.valueOf((String) eventData.get("pointY"));
        return new PointEventData(seriesName, category, pointX, pointY);
    }

    private MousePosition getClickPosition(Map<String, Object> eventData) {
        Integer mouseX = null;
        if (eventData.get("mouseX") instanceof Integer) {
            mouseX = (Integer) eventData.get("mouseX");
        }
        Integer mouseY = null;
        if (eventData.get("mouseY") instanceof Integer) {
            mouseY = (Integer) eventData.get("mouseY");
        }
        if (mouseX != null && mouseY != null) {
            return new MousePosition(mouseX, mouseY);
        }
        return null;
    }

    /**
     * This class contain mouse coordinates when a click event occurs on a
     * chart, a series or a point.
     *
     * The mouse coordinates are in pixels.
     *
     * @author Invient
     *
     */
    public final class MousePosition implements Serializable {
        private int mouseX;
        private int mouseY;

        /**
         * Creates this object with given arguments.
         *
         * @param mouseX
         *            x position of mouse when a click event occurred, in pixel
         * @param mouseY
         *            y position of mouse when a click event occurred, in pixel
         */
        public MousePosition(int mouseX, int mouseY) {
            this.mouseX = mouseX;
            this.mouseY = mouseY;
        }

        /**
         * Returns x position of mouse when a click event occurred, in pixel
         *
         * @return
         */
        public int getMouseX() {
            return mouseX;
        }

        /**
         * x position of mouse when a click event occurred, in pixel
         *
         * @return
         */
        public int getMouseY() {
            return mouseY;
        }

        @Override
        public String toString() {
            return "MousePosition [mouseX=" + mouseX + ", mouseY=" + mouseY
                    + "]";
        }

    }

    private final class PointEventData implements Serializable {
        private String seriesName;
        private String category;
        private double pointX;
        private double pointY;

        public PointEventData(String seriesName, String category,
                double pointX, double pointY) {
            super();
            this.seriesName = seriesName;
            this.category = category;
            this.pointX = pointX;
            this.pointY = pointY;
        }

        public String getSeriesName() {
            return seriesName;
        }

        public String getCategory() {
            return category;
        }

        public double getPointX() {
            return pointX;
        }

        public double getPointY() {
            return pointY;
        }

        @Override
        public String toString() {
            return "PointEventData [seriesName=" + seriesName + ", category="
                    + category + ", pointX=" + pointX + ", pointY=" + pointY
                    + "]";
        }

    }

    /***************************** POINT CLICK EVENT *****************************/
    /**
     * Click event. This event is thrown, when any point of this chart is
     * clicked and the point marker is enabled. The point marker is enabled by
     * default.
     *
     * @author Invient
     */
    public class PointClickEvent extends Component.Event {

        private String category;
        private Point point;
        private InvientCharts chart;
        private MousePosition mousePosition;

        /**
         * New instance of the point click event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param category
         *            a category to which point is associated in case of
         *            categorized axis,
         * @param point
         *            the point on which the click event occurred
         * @param mousePosition
         *            the position of a mouse when the click event occurred
         */
        public PointClickEvent(Component source, InvientCharts chart,
                String category, Point point, MousePosition mousePosition) {
            super(source);
            this.chart = chart;
            this.category = category;
            this.point = point;
            this.mousePosition = mousePosition;
        }

        /**
         * Returns a category to which point is associated in case of
         * categorized axis only.
         *
         * @return
         */
        public String getCategory() {
            return category;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return chart;
        }

        /**
         * Returns the point on which the click event occurred
         *
         * @return
         */
        public Point getPoint() {
            return this.point;
        }

        /**
         * Returns the position of a mouse when the click event occurred
         *
         * @return
         */
        public MousePosition getMousePosition() {
            return mousePosition;
        }

    }

    /**
     * Interface for listening for a {@link PointClickEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface PointClickListener extends Serializable {
        public void pointClick(PointClickEvent pointClickEvent);
    }

    private Map<SeriesType, Set<PointClickListener>> pointClickListeners = new HashMap<SeriesType, Set<PointClickListener>>();

    /**
     * Adds the point click listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(PointClickListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (pointClickListeners.containsKey(seriesType)) {
                pointClickListeners.get(seriesType).add(listener);
            } else {
                Set<PointClickListener> listeners = new HashSet<PointClickListener>();
                listeners.add(listener);
                pointClickListeners.put(seriesType, listeners);
            }
        }
        addListener(PointClickEvent.class, listener, POINT_CLICK_METHOD);
    }

    /**
     * Removes the point click listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(PointClickListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (pointClickListeners.containsKey(seriesType)) {
                pointClickListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(PointClickEvent.class, listener, POINT_CLICK_METHOD);
    }

    /**
     * Point remove event. This event is thrown, when any point of this chart is
     * removed from its series.
     *
     * This event is EXPERIMENTAL ONLY.
     *
     * @author Invient
     */
    public class PointRemoveEvent extends Component.Event {

        private String category;
        private Point point;
        private InvientCharts chart;

        /**
         * New instance of the point remove event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param category
         *            a category to which point is associated in case of
         *            categorized axis,
         * @param point
         *            the point removed
         */
        public PointRemoveEvent(Component source, InvientCharts chart,
                String category, Point point) {
            super(source);
            this.chart = chart;
            this.category = category;
            this.point = point;
        }

        /**
         * Returns a category to which point is associated in case of
         * categorized axis only.
         *
         * @return
         */
        public String getCategory() {
            return category;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return chart;
        }

        /**
         * Returns the point which has been removed
         *
         * @return
         */
        public Point getPoint() {
            return this.point;
        }

    }

    /**
     * Interface for listening for a {@link PointRemoveEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface PointRemoveListener extends Serializable {
        public void pointRemove(PointRemoveEvent pointRemoveEvent);
    }

    private Map<SeriesType, Set<PointRemoveListener>> pointRemoveListeners = new HashMap<SeriesType, Set<PointRemoveListener>>();

    /**
     * Adds the point remove listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(PointRemoveListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }
        for (SeriesType seriesType : seriesTypes) {
            if (pointRemoveListeners.containsKey(seriesType)) {
                pointRemoveListeners.get(seriesType).add(listener);
            } else {
                Set<PointRemoveListener> listeners = new HashSet<PointRemoveListener>();
                listeners.add(listener);
                pointRemoveListeners.put(seriesType, listeners);
            }
        }
        addListener(PointRemoveEvent.class, listener, POINT_REMOVE_METHOD);
    }

    /**
     * Removes the point remove listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(PointRemoveListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }
        for (SeriesType seriesType : seriesTypes) {
            if (pointRemoveListeners.containsKey(seriesType)) {
                pointRemoveListeners.get(seriesType).remove(listener);
            }
        }
        pointRemoveListeners.remove(listener);
        removeListener(PointRemoveEvent.class, listener, POINT_REMOVE_METHOD);
    }

    /**
     * Poin unselect event. This event is thrown, when any point of this chart
     * is unselected and the point marker is enabled. The point marker is
     * enabled by default.
     *
     * @author Invient
     */
    public class PointUnselectEvent extends Component.Event {

        private String category;
        private Point point;
        private InvientCharts chart;

        /**
         * New instance of the point unselect event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param category
         *            a category to which point is associated in case of
         *            categorized axis,
         * @param point
         *            the point unselected as a result of this event
         */
        public PointUnselectEvent(Component source, InvientCharts chart,
                String category, Point point) {
            super(source);
            this.chart = chart;
            this.category = category;
            this.point = point;
        }

        /**
         * Returns a category to which point is associated in case of
         * categorized axis only.
         *
         * @return
         */
        public String getCategory() {
            return category;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return chart;
        }

        /**
         * Returns the unselected point
         *
         * @return
         */
        public Point getPoint() {
            return this.point;
        }

    }

    /**
     * Interface for listening for a {@link PointUnselectEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface PointUnselectListener extends Serializable {
        public void pointUnSelect(PointUnselectEvent pointUnSelectEvent);
    }

    private Map<SeriesType, Set<PointUnselectListener>> pointUnselectListeners = new HashMap<SeriesType, Set<PointUnselectListener>>();

    /**
     * Adds the point unselect listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(PointUnselectListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }
        for (SeriesType seriesType : seriesTypes) {
            if (pointUnselectListeners.containsKey(seriesType)) {
                pointUnselectListeners.get(seriesType).add(listener);
            } else {
                Set<PointUnselectListener> listeners = new HashSet<PointUnselectListener>();
                listeners.add(listener);
                pointUnselectListeners.put(seriesType, listeners);
            }
        }
        addListener(PointUnselectEvent.class, listener, POINT_UNSELECT_METHOD);
    }

    /**
     * Removes the point unselect listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(PointUnselectListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }
        for (SeriesType seriesType : seriesTypes) {
            if (pointUnselectListeners.containsKey(seriesType)) {
                pointUnselectListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(PointUnselectEvent.class, listener,
                POINT_UNSELECT_METHOD);
    }

    /**
     * Point select event. This event is thrown, when any point of this chart is
     * selected and the point marker is enabled. The point marker is enabled by
     * default.
     *
     * @author Invient
     */
    public class PointSelectEvent extends Component.Event {

        private String category;
        private Point point;
        private InvientCharts chart;

        /**
         * New instance of the point select event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param category
         *            a category to which point is associated in case of
         *            categorized axis,
         * @param point
         *            the point selected as a result of this event
         */
        public PointSelectEvent(Component source, InvientCharts chart,
                String category, Point point) {
            super(source);
            this.chart = chart;
            this.category = category;
            this.point = point;
        }

        /**
         * Returns a category to which point is associated in case of
         * categorized axis only.
         *
         * @return
         */
        public String getCategory() {
            return category;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return chart;
        }

        /**
         * Returns the selected point
         *
         * @return
         */
        public Point getPoint() {
            return this.point;
        }

    }

    /**
     * Interface for listening for a {@link PointSelectListener} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface PointSelectListener extends Serializable {
        public void pointSelected(PointSelectEvent pointSelectEvent);
    }

    private Map<SeriesType, Set<PointSelectListener>> pointSelectListeners = new HashMap<SeriesType, Set<PointSelectListener>>();

    /**
     * Adds the point select listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(PointSelectListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (pointSelectListeners.containsKey(seriesType)) {
                pointSelectListeners.get(seriesType).add(listener);
            } else {
                Set<PointSelectListener> listeners = new HashSet<PointSelectListener>();
                listeners.add(listener);
                pointSelectListeners.put(seriesType, listeners);
            }
        }
        addListener(PointSelectEvent.class, listener, POINT_SELECT_METHOD);
    }

    /**
     * Removes the point select listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(PointSelectListener listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }
        for (SeriesType seriesType : seriesTypes) {
            if (pointSelectListeners.containsKey(seriesType)) {
                pointSelectListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(PointSelectEvent.class, listener, POINT_SELECT_METHOD);
    }

    private static final Method POINT_CLICK_METHOD;
    private static final Method POINT_REMOVE_METHOD;
    private static final Method POINT_SELECT_METHOD;
    private static final Method POINT_UNSELECT_METHOD;

    static {
        try {
            POINT_CLICK_METHOD = PointClickListener.class.getDeclaredMethod(
                    "pointClick", new Class[] { PointClickEvent.class });
            POINT_REMOVE_METHOD = PointRemoveListener.class.getDeclaredMethod(
                    "pointRemove", new Class[] { PointRemoveEvent.class });
            POINT_SELECT_METHOD = PointSelectListener.class.getDeclaredMethod(
                    "pointSelected", new Class[] { PointSelectEvent.class });
            POINT_UNSELECT_METHOD = PointUnselectListener.class
                    .getDeclaredMethod("pointUnSelect",
                            new Class[] { PointUnselectEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should not happen
            throw new java.lang.RuntimeException(
                    "Internal error finding methods in Button");
        }
    }

    // ***************************** Series Events ****************************/

    /**
     * Series click event. This event is thrown, when any series of this chart
     * is clicked.
     *
     * @author Invient
     */
    public class SeriesClickEvent extends Component.Event {
        private Point point;
        private Series series;
        private InvientCharts chart;
        private MousePosition mousePosition;

        /**
         * New instance of the series click event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param series
         *            the series on which click event occurred
         * @param point
         *            the closest point of a series
         * @param mousePosition
         *            the position of a mouse when the click event occurred
         */
        public SeriesClickEvent(Component source, InvientCharts chart,
                Series series, Point point, MousePosition mousePosition) {
            super(source);
            this.chart = chart;
            this.series = series;
            this.point = point;
            this.mousePosition = mousePosition;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * Returns the series object on which the click event occurred
         *
         * @return
         */
        public Series getSeries() {
            return this.series;
        }

        /**
         * Returns the closest point of a series
         *
         * @return
         */
        public Point getNearestPoint() {
            return this.point;
        }

        /**
         * Returns the position of a mouse when the click event occurred
         *
         * @return
         */
        public MousePosition getMousePosition() {
            return mousePosition;
        }

    }

    /**
     * Interface for listening for a {@link SeriesClickListerner} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface SeriesClickListerner extends Serializable {
        public void seriesClick(SeriesClickEvent seriesClickEvent);
    }

    private Map<SeriesType, Set<SeriesClickListerner>> seriesClickListeners = new HashMap<SeriesType, Set<SeriesClickListerner>>();

    /**
     * Adds the series click listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(SeriesClickListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesClickListeners.containsKey(seriesType)) {
                seriesClickListeners.get(seriesType).add(listener);
            } else {
                Set<SeriesClickListerner> listeners = new HashSet<SeriesClickListerner>();
                listeners.add(listener);
                seriesClickListeners.put(seriesType, listeners);
            }
        }
        addListener(SeriesClickEvent.class, listener, SERIES_CLICK_METHOD);
    }

    /**
     * Removes the series click listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(SeriesClickListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesClickListeners.containsKey(seriesType)) {
                seriesClickListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(SeriesClickEvent.class, listener, SERIES_CLICK_METHOD);
    }

    /**
     * Series Hide event. This event is thrown, when any series of this chart is
     * hidden.
     *
     * @author Invient
     */
    public class SeriesHideEvent extends Component.Event {
        private Series series;
        private InvientCharts chart;

        /**
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param series
         *            the series which got hidden
         */
        public SeriesHideEvent(Component source, InvientCharts chart,
                Series series) {
            super(source);
            this.chart = chart;
            this.series = series;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * Returns the series which got hidden
         *
         * @return
         */
        public Series getSeries() {
            return this.series;
        }
    }

    /**
     * Interface for listening for a {@link SeriesHideEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface SeriesHideListerner extends Serializable {
        public void seriesHide(SeriesHideEvent seriesHideEvent);
    }

    private Map<SeriesType, Set<SeriesHideListerner>> seriesHideListeners = new HashMap<SeriesType, Set<SeriesHideListerner>>();

    /**
     * Adds the series hide listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(SeriesHideListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesHideListeners.containsKey(seriesType)) {
                seriesHideListeners.get(seriesType).add(listener);
            } else {
                Set<SeriesHideListerner> listeners = new HashSet<SeriesHideListerner>();
                listeners.add(listener);
                seriesHideListeners.put(seriesType, listeners);
            }
        }
        addListener(SeriesHideEvent.class, listener, SERIES_HIDE_METHOD);
    }

    /**
     * Removes the series hide listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(SeriesHideListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesHideListeners.containsKey(seriesType)) {
                seriesHideListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(SeriesHideEvent.class, listener, SERIES_HIDE_METHOD);
    }

    /**
     * Series show event. This event is thrown, when any series of this chart is
     * displayed after a chart is created.
     *
     * @author Invient
     */
    public class SeriesShowEvent extends Component.Event {
        private Series series;
        private InvientCharts chart;

        /**
         * New instance of the series show event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param series
         *            the series which got displayed
         */
        public SeriesShowEvent(Component source, InvientCharts chart,
                Series series) {
            super(source);
            this.chart = chart;
            this.series = series;
        }

        /**
         * Returns the chart object associated with the series
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * the series which got displayed
         *
         * @return
         */
        public Series getSeries() {
            return this.series;
        }
    }

    /**
     * Interface for listening for a {@link SeriesShowEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface SeriesShowListerner extends Serializable {
        public void seriesShow(SeriesShowEvent seriesShowEvent);
    }

    private Map<SeriesType, Set<SeriesShowListerner>> seriesShowListeners = new HashMap<SeriesType, Set<SeriesShowListerner>>();

    /**
     * Adds the series show listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(SeriesShowListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesShowListeners.containsKey(seriesType)) {
                seriesShowListeners.get(seriesType).add(listener);
            } else {
                Set<SeriesShowListerner> listeners = new HashSet<SeriesShowListerner>();
                listeners.add(listener);
                seriesShowListeners.put(seriesType, listeners);
            }
        }
        addListener(SeriesShowEvent.class, listener, SERIES_SHOW_METHOD);
    }

    /**
     * Removes the series show listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(SeriesShowListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesShowListeners.containsKey(seriesType)) {
                seriesShowListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(SeriesShowEvent.class, listener, SERIES_SHOW_METHOD);
    }

    // LEGENDITEMCLICK
    // This event occurs when a series is clicked in the legend.
    // This event is not applicable for PieChart instead use
    // LegendItemClickEvent/LegendItemClickListener
    /**
     * Series legend item click event. This event is thrown, when legend item is
     * clicked. This event is not applicable for PieChart instead use
     * {@link LegendItemClickEvent}
     *
     * @author Invient
     */
    public class SeriesLegendItemClickEvent extends Component.Event {
        private Series series;
        private InvientCharts chart;

        /**
         * New instance of the point click event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param series
         *            the series associated with the legend item
         */
        public SeriesLegendItemClickEvent(Component source,
                InvientCharts chart, Series series) {
            super(source);
            this.chart = chart;
            this.series = series;
        }

        /**
         * Returns the chart object associated with the series
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * Returns the series associated with the legend item
         *
         * @return
         */
        public Series getSeries() {
            return this.series;
        }
    }

    /**
     * Interface for listening for a {@link SeriesLegendItemClickEvent}
     * triggered by {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface SeriesLegendItemClickListerner extends Serializable {
        public void seriesLegendItemClick(
                SeriesLegendItemClickEvent seriesLegendItemClickEvent);
    }

    private Map<SeriesType, Set<SeriesLegendItemClickListerner>> seriesLegendItemClickListeners = new HashMap<SeriesType, Set<SeriesLegendItemClickListerner>>();

    /**
     * Adds the series legend item click listener. If the argument seriesTypes
     * is not specified then the listener will be added for all series type
     * otherwise it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(SeriesLegendItemClickListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }

        for (SeriesType seriesType : seriesTypes) {
            if (seriesLegendItemClickListeners.containsKey(seriesType)) {
                seriesLegendItemClickListeners.get(seriesType).add(listener);
            } else {
                Set<SeriesLegendItemClickListerner> listeners = new HashSet<SeriesLegendItemClickListerner>();
                listeners.add(listener);
                seriesLegendItemClickListeners.put(seriesType, listeners);
            }
        }
        addListener(SeriesLegendItemClickEvent.class, listener,
                SERIES_LEGENDITEM_CLICK_METHOD);
    }

    /**
     * Removes the series legend item click listener. If the argument
     * seriesTypes is not specified then the listener will be removed only for a
     * series type SeriesType.COMMONSERIES otherwise the listener will be
     * removed for all specified series types.
     *
     * @param listener
     *            the listener to be removed
     * @param seriesTypes
     *            one or more series types as defined by (@link SeriesType}
     */
    public void removeListener(SeriesLegendItemClickListerner listener,
            SeriesType... seriesTypes) {
        if (seriesTypes.length == 0) {
            seriesTypes = new SeriesType[] { SeriesType.COMMONSERIES };
        }
        for (SeriesType seriesType : seriesTypes) {
            if (seriesLegendItemClickListeners.containsKey(seriesType)) {
                seriesLegendItemClickListeners.get(seriesType).remove(listener);
            }
        }
        removeListener(SeriesLegendItemClickEvent.class, listener,
                SERIES_LEGENDITEM_CLICK_METHOD);
    }

    private static final Method SERIES_CLICK_METHOD;
    // private static final Method SERIES_CHECKBOX_CLICK_METHOD;
    private static final Method SERIES_HIDE_METHOD;
    private static final Method SERIES_SHOW_METHOD;
    private static final Method SERIES_LEGENDITEM_CLICK_METHOD;

    static {
        try {
            SERIES_CLICK_METHOD = SeriesClickListerner.class.getDeclaredMethod(
                    "seriesClick", new Class[] { SeriesClickEvent.class });
            // SERIES_CHECKBOX_CLICK_METHOD = SeriesCheckboxClickListerner.class
            // .getDeclaredMethod("seriesCheckboxClick",
            // new Class[] { SeriesCheckboxClickEvent.class });
            SERIES_HIDE_METHOD = SeriesHideListerner.class.getDeclaredMethod(
                    "seriesHide", new Class[] { SeriesHideEvent.class });
            SERIES_SHOW_METHOD = SeriesShowListerner.class.getDeclaredMethod(
                    "seriesShow", new Class[] { SeriesShowEvent.class });
            SERIES_LEGENDITEM_CLICK_METHOD = SeriesLegendItemClickListerner.class
                    .getDeclaredMethod("seriesLegendItemClick",
                            new Class[] { SeriesLegendItemClickEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error finding methods in Button");
        }
    }

    /**************************** PieChart related events ****************************/
    // PieChart LEGENDITEMCLICK
    // This event occurs when a point of a PieChart is clicked

    /**
     * PieChart legend item click event. This event is thrown, when the legend
     * item belonging to the pie point (slice) is clicked.
     *
     * @author Invient
     */
    public class PieChartLegendItemClickEvent extends Component.Event {

        private InvientCharts chart;
        private Point point;

        /**
         * New instance of the piechart legend item click event
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param point
         *            the pie point (slice) associated with the legend item
         */
        public PieChartLegendItemClickEvent(Component source,
                InvientCharts chart, Point point) {
            super(source);
            this.chart = chart;
            this.point = point;
        }

        /**
         * Returns the chart object associated with the point
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * Returns the pie point (slice) associated with the legend item
         *
         * @return
         */
        public Point getPoint() {
            return this.point;
        }
    }

    /**
     * Interface for listening for a {@link PieChartLegendItemClickEvent}
     * triggered by {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface PieChartLegendItemClickListener extends Serializable {
        public void legendItemClick(
                PieChartLegendItemClickEvent legendItemClickEvent);
    }

    private Set<PieChartLegendItemClickListener> pieChartLegendItemClickListener = new HashSet<PieChartLegendItemClickListener>();

    /**
     * Adds the piechart legend item click listener. If the argument seriesTypes
     * is not specified then the listener will be added for all series type
     * otherwise it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(PieChartLegendItemClickListener listener) {
        pieChartLegendItemClickListener.add(listener);
        addListener(PieChartLegendItemClickEvent.class, listener,
                LEGENDITEM_CLICK_METHOD);
    }

    /**
     * Removes the piechart legend item click listener. If the argument
     * seriesTypes is not specified then the listener will be removed only for a
     * series type SeriesType.COMMONSERIES otherwise the listener will be
     * removed for all specified series types.
     *
     * @param listener
     *            the listener to be removed
     */
    public void removeListener(PieChartLegendItemClickListener listener) {
        pieChartLegendItemClickListener.remove(listener);
        removeListener(PieChartLegendItemClickEvent.class, listener,
                LEGENDITEM_CLICK_METHOD);
    }

    private static final Method LEGENDITEM_CLICK_METHOD;

    static {
        try {
            LEGENDITEM_CLICK_METHOD = PieChartLegendItemClickListener.class
                    .getDeclaredMethod("legendItemClick",
                            new Class[] { PieChartLegendItemClickEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error finding methods in Button");
        }
    }

    /***************************** Chart Events *****************************/
    /**
     * Chart Click event. This event is thrown, when this chart is clicked.
     *
     * @author Invient
     */
    public class ChartClickEvent extends Component.Event {
        private InvientCharts chart;
        private Point point;
        private MousePosition mousePosition;

        /**
         * New instance of the chart click event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param point
         *            the position where the click event occurred in axes units
         * @param mousePosition
         *            the coordinate of mouse where the click event occurred in
         *            pixels
         */
        public ChartClickEvent(Component source, InvientCharts chart,
                Point point, MousePosition mousePosition) {
            super(source);
            this.chart = chart;
            this.point = point;
            this.mousePosition = mousePosition;
        }

        /**
         * Returns the chart object on which the click event occurred
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * Returns the point representing the position where the click event
         * occurred in axes units
         *
         * @return
         */
        public Point getPoint() {
            return this.point;
        }

        /**
         * Returns the position of a mouse when the click event occurred
         *
         * @return
         */
        public MousePosition getMousePosition() {
            return mousePosition;
        }

        @Override
        public String toString() {
            return "ChartClickEvent [point=" + point + ", mousePosition="
                    + mousePosition + "]";
        }

    }

    /**
     * Interface for listening for a {@link ChartClickEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface ChartClickListener extends Serializable {
        public void chartClick(ChartClickEvent chartClickEvent);
    }

    private Set<ChartClickListener> chartClickListener = new HashSet<ChartClickListener>();

    /**
     * Adds the chart click listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(ChartClickListener listener) {
        chartClickListener.add(listener);
        addListener(ChartClickEvent.class, listener, CHART_CLICK_METHOD);
    }

    /**
     * Removes the chart click listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     */
    public void removeListener(ChartClickListener listener) {
        chartClickListener.remove(listener);
        removeListener(ChartClickEvent.class, listener, CHART_CLICK_METHOD);
    }

    /**
     * Add series event. This event is thrown, when a series is added to the
     * chart.
     *
     * @author Invient
     */
    public class ChartAddSeriesEvent extends Component.Event {
        private InvientCharts chart;

        /**
         * New instance of the chart add series event.
         *
         * @param source
         * @param chart
         */
        public ChartAddSeriesEvent(Component source, InvientCharts chart) {
            super(source);
            this.chart = chart;
        }

        /**
         * Returns the chart object to which a series is added
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }
    }

    /**
     * Interface for listening for a {@link ChartAddSeriesEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface ChartAddSeriesListener extends Serializable {
        public void chartAddSeries(ChartAddSeriesEvent chartAddSeriesEvent);
    }

    private Set<ChartAddSeriesListener> chartAddSeriesListener = new HashSet<ChartAddSeriesListener>();

    /**
     * Adds the series add listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(ChartAddSeriesListener listener) {
        chartAddSeriesListener.add(listener);
        addListener(ChartAddSeriesEvent.class, listener,
                CHART_ADD_SERIES_METHOD);
    }

    /**
     * Removes the series add listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     */
    public void removeListener(ChartAddSeriesListener listener) {
        chartAddSeriesListener.remove(listener);
        removeListener(ChartAddSeriesEvent.class, listener,
                CHART_ADD_SERIES_METHOD);
    }

    /**
     * Defines information on the selected area.
     *
     * @author Invient
     *
     */
    public final class ChartArea implements Serializable {
        private double xAxisMin;
        private double xAxisMax;
        private double yAxisMin;
        private double yAxisMax;

        public ChartArea(double xAxisMin, double xAxisMax, double yAxisMin,
                double yAxisMax) {
            this.xAxisMin = xAxisMin;
            this.xAxisMax = xAxisMax;
            this.yAxisMin = yAxisMin;
            this.yAxisMax = yAxisMax;
        }

        public double getxAxisMin() {
            return xAxisMin;
        }

        public double getxAxisMax() {
            return xAxisMax;
        }

        public double getyAxisMin() {
            return yAxisMin;
        }

        public double getyAxisMax() {
            return yAxisMax;
        }

        @Override
        public String toString() {
            return "ChartSelectedArea [xAxisMin=" + xAxisMin + ", xAxisMax="
                    + xAxisMax + ", yAxisMin=" + yAxisMin + ", yAxisMax="
                    + yAxisMax + "]";
        }

    }

    /**
     * Chart zoom event. This event is thrown, when an area of the chart has
     * been selected.
     *
     * @author Invient
     */
    public class ChartZoomEvent extends Component.Event {
        private InvientCharts chart;
        private ChartArea chartArea;

        /**
         * New instance of the chart zoom event.
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         * @param chartArea
         *            the chartArea object containing dimensions of zoomed area
         *            of the chart
         */
        public ChartZoomEvent(Component source, InvientCharts chart,
                ChartArea chartArea) {
            super(source);
            this.chart = chart;
            this.chartArea = chartArea;
        }

        /**
         * Returns the chart object for which the zoom event has occurred
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }

        /**
         * Returns the chartArea object containing dimensions of zoomed area of
         * the chart
         *
         * @return
         */
        public ChartArea getChartArea() {
            return this.chartArea;
        }
    }

    /**
     * Interface for listening for a {@link ChartZoomEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface ChartZoomListener extends Serializable {
        public void chartZoom(ChartZoomEvent chartZoomEvent);
    }

    private Set<ChartZoomListener> chartZoomListener = new HashSet<ChartZoomListener>();

    /**
     * Adds the chart zoom listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(ChartZoomListener listener) {
        chartZoomListener.add(listener);
        addListener(ChartZoomEvent.class, listener, CHART_ZOOM_METHOD);
    }

    /**
     * Removes the chart zoom listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     */
    public void removeListener(ChartZoomListener listener) {
        chartZoomListener.remove(listener);
        removeListener(ChartZoomEvent.class, listener, CHART_ZOOM_METHOD);
    }

    /**
     * Chart reset zoom event. This event is thrown, when a chart is reset by
     * setting its zoom level to normal.
     *
     * @author Invient
     */
    public class ChartResetZoomEvent extends Component.Event {
        private InvientCharts chart;

        /**
         * New instance of the chart reset zoom event
         *
         * @param source
         *            the chart object itself
         * @param chart
         *            the chart object itself
         */
        public ChartResetZoomEvent(Component source, InvientCharts chart) {
            super(source);
            this.chart = chart;
        }

        /**
         * Returns the chart object for which zoom has been reset to normal
         *
         * @return
         */
        public InvientCharts getChart() {
            return this.chart;
        }
    }

    /**
     * Interface for listening for a {@link ChartResetZoomEvent} triggered by
     * {@link InvientCharts}
     *
     * @author Invient
     *
     */
    public interface ChartResetZoomListener extends Serializable {
        public void chartResetZoom(ChartResetZoomEvent chartResetZoomEvent);
    }

    private Set<ChartResetZoomListener> chartResetZoomListener = new HashSet<ChartResetZoomListener>();

    /**
     * Adds the chart reset zoom listener. If the argument seriesTypes is not
     * specified then the listener will be added for all series type otherwise
     * it will be added for a specific series type
     *
     * @param listener
     *            the Listener to be added.
     */
    public void addListener(ChartResetZoomListener listener) {
        chartResetZoomListener.add(listener);
        addListener(ChartResetZoomEvent.class, listener,
                CHART_RESET_ZOOM_METHOD);
    }

    /**
     * Removes the chart reset zoom listener. If the argument seriesTypes is not
     * specified then the listener will be removed only for a series type
     * SeriesType.COMMONSERIES otherwise the listener will be removed for all
     * specified series types.
     *
     * @param listener
     *            the listener to be removed
     */
    public void removeListener(ChartResetZoomListener listener) {
        chartResetZoomListener.remove(listener);
        removeListener(ChartResetZoomEvent.class, listener,
                CHART_RESET_ZOOM_METHOD);
    }

    private static final Method CHART_CLICK_METHOD;
    private static final Method CHART_ADD_SERIES_METHOD;
    private static final Method CHART_ZOOM_METHOD;
    private static final Method CHART_RESET_ZOOM_METHOD;

    static {
        try {
            CHART_CLICK_METHOD = ChartClickListener.class.getDeclaredMethod(
                    "chartClick", new Class[] { ChartClickEvent.class });
            CHART_ADD_SERIES_METHOD = ChartAddSeriesListener.class
                    .getDeclaredMethod("chartAddSeries",
                            new Class[] { ChartAddSeriesEvent.class });
            CHART_ZOOM_METHOD = ChartZoomListener.class.getDeclaredMethod(
                    "chartZoom", new Class[] { ChartZoomEvent.class });
            CHART_RESET_ZOOM_METHOD = ChartResetZoomListener.class
                    .getDeclaredMethod("chartResetZoom",
                            new Class[] { ChartResetZoomEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error finding methods in Button");
        }
    }

    // *************************************************************************//
    // **************************** Chart Container
    // ****************************//
    // *************************************************************************//
    private LinkedHashSet<Series> chartSeries = new LinkedHashSet<Series>();
    private boolean reloadChartSeries = false;

    /**
     * The data of a chart is defined in terms of {@link Series}. This method
     * removes all previously set series of this chart and adds the argument
     * series. If the argument series is null then no actions are taken.
     *
     * @param series
     *            A collection of series to set as chart's data
     */
    public void setSeries(LinkedHashSet<Series> series) {
        if (series != null) {
            reloadChartSeries = true;
            this.chartSeries.clear();
            this.seriesCURSet.clear();
            for (Series seriesData : series) {
                addSeries(seriesData);
            }
        }
    }

    /**
     * Returns a series whose name matches the argument name.
     *
     * @param name
     *            the name of the series
     * @return
     */
    public Series getSeries(String name) {
        for (Series series : this.chartSeries) {
            if (series.getName().equals(name)) {
                return series;
            }
        }
        return null;
    }

    /**
     * Adds the argument series to this chart.
     *
     * @param seriesData
     *            the series to be added
     */
    public void addSeries(Series seriesData) {
        if (this.chartSeries.add(seriesData)) {
            seriesData.setInvientCharts(this);
            addSeriesCUROperation(new SeriesCUR(SeriesCURType.ADD,
                    seriesData.getName()));
            requestRepaint();
        }
    }

    /**
     * Removes a series whose name matches the argument name.
     *
     * @param name
     *            the name of the series
     */
    public void removeSeries(String name) {
        for (Iterator<Series> seriesItr = this.chartSeries.iterator(); seriesItr
                .hasNext();) {
            Series series = seriesItr.next();
            if (series.getName().equals(name)) {
                seriesItr.remove();
                series.setInvientCharts(null);
                addSeriesCUROperation(new SeriesCUR(SeriesCURType.REMOVE,
                        series.getName()));
                requestRepaint();
            }
        }
    }

    /**
     * Removes the argument seriesData from this chart.
     *
     * @param seriesData
     *            the series object to be removed
     */
    public void removeSeries(Series seriesData) {
        if (this.chartSeries.remove(seriesData)) {
            seriesData.setInvientCharts(null);
            addSeriesCUROperation(new SeriesCUR(SeriesCURType.REMOVE,
                    seriesData.getName()));
            requestRepaint();
        }
    }

    /**
     * This class represents a point of the chart's series. A series can have
     * one or more points. A point has (X, Y) coordinates. None of the
     * coordinates are mandatory. The name of a point can be displayed in a
     * tooltip.
     *
     * To represent no activity or missing points in the chart, create a point
     * with both X and Y as null or just Y as null.
     *
     * It is possible to specify custom configuration for each point. e.g. If a
     * highest point can be marked in a chart with a different color using this
     * configuration.
     *
     * A point cannot be created without a series. It must belong to a series.
     * However, the point must be added to a series by calling Series.addPoint()
     * or Series.setPoints() to permanently add point to the series.
     *
     * @author Invient
     *
     * @see DecimalPoint
     * @see DateTimePoint
     * @see PointConfig
     *
     */
    public static abstract class Point implements Serializable {
        private String id;
        private String name;
        private Series series;
        private PointConfig config;
        private boolean isAutosetX;

        /**
         * Creates a point with given arguments.
         *
         * @param series
         *            The series to which the point must be associated.
         * @exception IllegalArgumentException
         *                If the argument series is null
         *
         */
        public Point(Series series) {
            if (series == null) {
                throw new IllegalArgumentException(
                        "A point cannot be created without a series.");
            }
            this.series = series;
        }

        /**
         * To allow creation of a point from inside of InvientCharts component
         */
        private Point() {
            // FIXME this is not a correct way of doing it.
        }

        /**
         * Creates a point with given arguments.
         *
         * @param series
         *            The series to which the point must be associated.
         * @param config
         *            The configuration for this point, if any
         * @exception IllegalArgumentException
         *                If the argument series is null
         */
        public Point(Series series, PointConfig config) {
            this(series);
            this.config = config;
        }

        /**
         * Creates a point with given arguments.
         *
         * @param series
         *            The series to which the point must be associated.
         * @param name
         *            name of this point
         * @exception IllegalArgumentException
         *                If the argument series is null
         */
        public Point(Series series, String name) {
            this(series);
            this.name = name;
        }

        /**
         * Creates a point with given arguments.
         *
         * @param series
         *            The series to which the point must be associated.
         * @param name
         *            name of this point
         * @param config
         *            The configuration for this point, if any
         * @exception IllegalArgumentException
         *                If the argument series is null
         */
        public Point(Series series, String name, PointConfig config) {
            this(series, name);
            this.config = config;
        }

        String getId() {
            return id;
        }

        /**
         * Returns name of this point
         *
         * @return
         */
        public String getName() {
            return name;
        }

        /**
         * Sets name of this point
         *
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * Returns {@link Series} associated with this point
         *
         * @return
         */
        public Series getSeries() {
            return series;
        }

        /**
         * Returns {@link PointConfig} for this point
         *
         * @return
         */
        public PointConfig getConfig() {
            return config;
        }

        /**
         * Sets {@link PointConfig} for this point
         *
         */
        public void setConfig(PointConfig config) {
            this.config = config;
        }

        /**
         * Returns true if X value of this point is set programmatically
         *
         * @return
         */
        boolean isAutosetX() {
            return isAutosetX;
        }

        /**
         * If the argument is true it indicates that the X value of this point
         * is set programmatically and user has not specified it.
         *
         * @return
         */
        void setAutosetX(boolean isAutosetX) {
            this.isAutosetX = isAutosetX;
        }

        /**
         * Returns X value of this point
         *
         * @return
         */
        public abstract Object getX();

        /**
         * Returns Y value of this point
         *
         * @return
         */
        public abstract Object getY();

        @Override
        public String toString() {
            return "Point [id=" + id + ", name=" + name + ", series="
                    + series.getName() + ", config=" + config + "]";
        }

    }

    /**
     * This class represent a point with (X, Y) both as number. It should be
     * used to add points to {@link XYSeries}
     *
     * @author Invient
     *
     */
    public static final class DecimalPoint extends Point {
        private Double x;
        private Double y;

        /**
         * @param series
         *            the series to which this belongs to
         */
        public DecimalPoint(Series series) {
            super(series);
        }

        /**
         * @param series
         *            the series to which this point belongs to
         * @param y
         *            the y value of this point
         */
        public DecimalPoint(Series series, double y) {
            super(series);
            this.y = y;
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param name
         *            the name of this point
         * @param y
         *            the y value of this point
         */
        public DecimalPoint(Series series, String name, double y) {
            super(series, name);
            this.y = y;
        }

        /**
         * To allow creation of a point within the InvientChart.
         *
         * @param x
         * @param y
         */
        private DecimalPoint(double x, double y) {
            // FIXME this is not a correct way of doing it.
            super();
            this.x = x;
            this.y = y;
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param name
         *            the name for this point
         * @param y
         *            the y value of this point
         * @param config
         */
        public DecimalPoint(Series series, String name, double y,
                PointConfig config) {
            super(series, name, config);
            this.y = y;
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param y
         *            the y value of this point
         * @param config
         *            the configuration for this point
         */
        public DecimalPoint(Series series, double y, PointConfig config) {
            super(series, config);
            this.y = y;
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param x
         *            the x value of this point
         * @param y
         *            the y value of this point
         */
        public DecimalPoint(Series series, double x, double y) {
            this(series, x, y, null);
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param x
         *            the x value of this point
         * @param y
         *            the y value of this point
         * @param config
         *            the configuration of this point
         */
        public DecimalPoint(Series series, double x, double y,
                PointConfig config) {
            super(series, config);
            this.x = x;
            this.y = y;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Point#getX()
         */
        @Override
        public Double getX() {
            return x;
        }

        /**
         * Sets the x value of this point
         *
         * @param x
         */
        public void setX(Double x) {
            this.x = x;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Point#getY()
         */
        @Override
        public Double getY() {
            return y;
        }

        /**
         * Sets the y value of this point
         *
         * @param y
         */
        public void setY(Double y) {
            this.y = y;
        }

        @Override
        public String toString() {
            return "DecimalPoint [x=" + x + ", y=" + y + ", id=" + getId()
                    + ", name=" + getName() + ", seriesName="
                    + (getSeries() != null ? getSeries().getName() : "") + "]";
        }

    }

    /**
     * This class represent a point with (X, Y) both as number. It should be
     * used to add points to {@link DateTimeSeries}
     *
     * @author Invient
     *
     */
    public static final class DateTimePoint extends Point {
        private Date x;
        private Double y;

        /**
         * @param series
         *            the series to which this belongs to
         */
        public DateTimePoint(Series series) {
            super(series);
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param y
         *            the y value of this point
         */
        public DateTimePoint(Series series, double y) {
            this(series, "", y);
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param name
         *            the name of this point
         * @param y
         *            the y value of this point
         */
        public DateTimePoint(Series series, String name, double y) {
            super(series, name);
            this.y = y;
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param name
         *            the name of this point
         * @param y
         *            the y value of this point
         * @param config
         */
        public DateTimePoint(Series series, String name, double y,
                PointConfig config) {
            super(series, name, config);
            this.y = y;
        }

        /**
         * @param series
         *            the series to which this belongs to
         * @param x
         *            the x value of this point
         * @param y
         *            the y value of this point
         */
        public DateTimePoint(Series series, Date x, double y) {
            this(series, y);
            this.x = x;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Point#getX()
         */
        public Date getX() {
            return x;
        }

        /**
         * Sets the x value of this point
         *
         * @param x
         */
        public void setX(Date x) {
            this.x = x;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Point#getY()
         */
        public Double getY() {
            return y;
        }

        /**
         * Sets the y value of this point
         *
         * @param y
         */
        public void setY(Double y) {
            this.y = y;
        }

        @Override
        public String toString() {
            return "DateTimePoint [x=" + getDateInMilliseconds(x, true)
                    + ", y=" + y + ", id=" + getId() + ", name=" + getName()
                    + ", seriesName="
                    + (getSeries() != null ? getSeries().getName() : "") + "]";
        }

    }

    /**
     * This class defines a series of the chart. A series contains a collection
     * of points. Series can be one of types defined by {@link SeriesType}.
     *
     * Each series must have unique name. If an attempt is made to add two
     * series with same then only the first added series will be in effect.
     *
     * If the series type is not specified, it defaults to chart type and the
     * default chart type is SeriesType.LINE. A series has unique xAxis and
     * yAxis object associated with it. There is no need to set xAxis and yAxis
     * unless the chart has more than one one axis of any type and the series
     * must belong to any of the secondary axis.
     *
     * It is also possible to specify configuration for individual series and
     * not just series type.
     *
     * @author Invient
     *
     */
    public static abstract class Series implements Serializable {
        private LinkedHashSet points = new LinkedHashSet();
        private String name = "";
        private SeriesType type;
        private String stack;
        private XAxis xAxis;
        private YAxis yAxis;
        private SeriesConfig config;
        private InvientCharts invientCharts;

        /**
         * Creates a series with given name
         *
         * @param name
         *            the name of this series
         */
        public Series(String name) {
            this.name = name;
        }

        /**
         * Creates a series with given name and type
         *
         * @param name
         *            the name of this series
         * @param seriesType
         *            the type of this series
         */
        public Series(String name, SeriesType seriesType) {
            this(name);
            this.type = seriesType;
        }

        /**
         * Creates a series with given name and configuration
         *
         * @param name
         *            the name of this series
         * @param config
         *            the configuration for this series
         */
        public Series(String name, SeriesConfig config) {
            this(name);
            this.config = config;
        }

        /**
         * Creates a series with given name, type and configuration
         *
         * @param name
         *            the name of this series
         * @param seriesType
         *            the type of this series
         * @param config
         *            the configuration for this series
         */
        public Series(String name, SeriesType seriesType, SeriesConfig config) {
            this(name, config);
            this.type = seriesType;
        }

        /**
         * @return
         */
        public SeriesConfig getConfig() {
            return config;
        }

        /**
         * @return
         */
        public String getName() {
            return name;
        }

        /**
         * Sets name of this series
         *
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * @return
         */
        public SeriesType getType() {
            return type;
        }

        /**
         * Sets type of this series
         *
         * @param type
         */
        public void setType(SeriesType type) {
            this.type = type;
        }

        /**
         * @return
         */
        public String getStack() {
            return stack;
        }

        /**
         * By using this stack property, it is possible to group series in a
         * stacked chart. Sets stack for this series. If two series belongs to
         * the same stack then the resultant chart will be stacked chart
         *
         * @param stack
         */
        public void setStack(String stack) {
            this.stack = stack;
        }

        /**
         * @return
         */
        public XAxis getXAxis() {
            return xAxis;
        }

        /**
         * Sets xAxis of this series
         *
         * @param xAxis
         */
        public void setXAxis(XAxis xAxis) {
            this.xAxis = xAxis;
        }

        /**
         * @return
         */
        public YAxis getYAxis() {
            return yAxis;
        }

        /**
         * Sets yAxis of this series
         *
         * @param yAxis
         */
        public void setYAxis(YAxis yAxis) {
            this.yAxis = yAxis;
        }

        /**
         * @param points
         */
        protected void removePoint(Point... points) {
            for (Point point : points) {
                this.points.remove(point);
            }
        }

        /**
         * Removes all points in this series
         */
        protected void removeAllPoints() {
            this.points.clear();
        }

        /**
         * Adds one or more points into this series, specified as an argument to
         * this method
         *
         * @param points
         */
        protected void addPoint(Point... points) {
            for (Point point : points) {
                this.points.add(point);
            }
        }

        /**
         * Returns all points of this series
         *
         * @return
         */
        protected LinkedHashSet getPoints() {
            return this.points;
        }

        /**
         * Sets points into this series
         *
         * @param points
         */
        protected void setPoints(LinkedHashSet points) {
            if (points != null) {
                this.points = points;
            }
        }

        /**
         * Show this series
         */
        public void show() {
            this.config = (this.config == null ? new SeriesConfig()
                    : this.config);
            this.config.setVisible(true);
            this.invientCharts.addSeriesCUROperation(new SeriesCUR(
                    SeriesCURType.UPDATE, this.getName()));
            this.invientCharts.requestRepaint();
        }

        /**
         * Hide this series
         */
        public void hide() {
            this.config = (this.config == null ? new SeriesConfig()
                    : this.config);
            this.config.setVisible(false);
            this.invientCharts.addSeriesCUROperation(new SeriesCUR(
                    SeriesCURType.UPDATE, this.getName()));
            this.invientCharts.requestRepaint();
        }

        void setInvientCharts(InvientCharts invientCharts) {
            this.invientCharts = invientCharts;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Series other = (Series) obj;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }

        @Override
        public String toString() {
            return "Series [points=" + points + ", name=" + name + ", type="
                    + type + ", stack=" + stack + ", xAxis=" + xAxis
                    + ", yAxis=" + yAxis + ", config=" + config + "]";
        }
    }

    /**
     * This class defines a number series. In this series both X and Y values
     * must be number. To use date values, use {@link DateTimeSeries}
     *
     * @author Invient
     *
     * @see DateTimeSeries
     */
    public static class XYSeries extends Series {

        /**
         * Creates a series with given name
         *
         * @param name
         *            the name of this series
         */
        public XYSeries(String name) {
            super(name);
        }

        /**
         * Creates a series with given name and configuration
         *
         * @param name
         *            the name of this series
         * @param config
         *            the configuration for this series
         */
        public XYSeries(String name, SeriesConfig config) {
            super(name, config);
        }

        /**
         * Creates a series with given name and type
         *
         * @param name
         *            the name of this series
         * @param seriesType
         *            the type of this series
         */
        public XYSeries(String name, SeriesType seriesType) {
            super(name, seriesType);
        }

        /**
         * Creates a series with given name, type and configuration
         *
         * @param name
         *            the name of this series
         * @param seriesType
         *            the type of this series
         * @param config
         *            the configuration for this series
         */
        public XYSeries(String name, SeriesType seriesType, SeriesConfig config) {
            super(name, seriesType, config);
        }

        /**
         * Removes the specified point from the series
         *
         * @param points
         */
        public void removePoint(DecimalPoint... points) {
            super.removePoint(points);
            updatePointXValuesIfNotPresent();
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Series#removeAllPoints()
         */
        public void removeAllPoints() {
            super.removeAllPoints();
        }

        /**
         * Adds the specified point into the series
         *
         * @param points
         */
        public void addPoint(DecimalPoint... points) {
            super.addPoint(points);
            updatePointXValuesIfNotPresent();
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Series#getPoints()
         */
        public LinkedHashSet<DecimalPoint> getPoints() {
            return super.getPoints();
        }

        /**
         * Sets points into this series. This method removes all of its points
         * and then add points specified in the method argument. If the argument
         * is null then no actions are taken.
         *
         * @param points
         *            the collection of points to set into this series.
         */
        public void setSeriesPoints(LinkedHashSet<DecimalPoint> points) {
            super.setPoints(points);
            updatePointXValuesIfNotPresent();
        }

        /**
         *
         */
        private void updatePointXValuesIfNotPresent() {
            double pointStart = 0;
            double pointInterval = 1;
            if (super.getConfig() instanceof BaseLineConfig) {
                BaseLineConfig config = (BaseLineConfig) super.getConfig();
                if (config.getPointStart() != null) {
                    pointStart = config.getPointStart();
                }
                if (config.getPointInterval() != null) {
                    pointInterval = config.getPointInterval();
                }
            }
            int count = 0;
            for (DecimalPoint point : getPoints()) {
                if ((point.getX() == null || (point.getX() != null && point
                        .isAutosetX()))) {
                    point.setAutosetX(true);
                    if (count == 0) {
                        point.setX(pointStart);
                        count++;
                    } else {
                        pointStart = pointStart + pointInterval;
                        point.setX(pointStart);
                    }
                }
            }
        }

    }

    /**
     * This class defines a datetime series. In this series, the X value must be
     * date and Y values must be number. To use number values, use
     * {@link XYSeries}
     *
     * @author Invient
     *
     * @see XYSeries
     */
    public static class DateTimeSeries extends Series {
        private boolean includeTime;

        /**
         * Creates a series with given name
         *
         * @param name
         *            the name of this series
         */
        public DateTimeSeries(String name) {
            super(name);
        }

        /**
         * Creates a series with given name and configuration
         *
         * @param name
         *            the name of this series
         * @param config
         *            the configuration for this series
         */
        public DateTimeSeries(String name, SeriesConfig config) {
            super(name, config);
        }

        /**
         * Creates a series with given name and type
         *
         * @param name
         *            the name of this series
         * @param seriesType
         *            the type of this series
         */
        public DateTimeSeries(String name, SeriesType seriesType) {
            super(name, seriesType);
        }

        /**
         * Creates a series with given name, type and configuration
         *
         * @param name
         *            the name of this series
         * @param seriesType
         *            the type of this series
         * @param config
         *            the configuration for this series
         */
        public DateTimeSeries(String name, SeriesType seriesType,
                SeriesConfig config) {
            super(name, seriesType, config);
        }

        /**
         * Removes all points specified as method argument into this series
         *
         * @param points
         */
        public void removePoint(DateTimePoint... points) {
            super.removePoint(points);
            updatePointXValuesIfNotPresent();
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Series#removeAllPoints()
         */
        public void removeAllPoints() {
            super.removeAllPoints();
        }

        /**
         * Add all points specified as method argument into this series
         *
         * @param points
         */
        public void addPoint(DateTimePoint... points) {
            super.addPoint(points);
            updatePointXValuesIfNotPresent();
        }

        /**
         * Returns true if the time in the Y property of DateTimePoint will not
         * be considered when drawing the chart.
         *
         * @return
         */
        public boolean isIncludeTime() {
            return includeTime;
        }

        /**
         * If true then the time in the Y property of DateTimePoint will not be
         * considered when drawing the chart.
         *
         * @param includeTime
         */
        public void setIncludeTime(boolean includeTime) {
            this.includeTime = includeTime;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.invient.vaadin.chart.InvientChart.Series#getPoints()
         */
        public LinkedHashSet<DateTimePoint> getPoints() {
            return super.getPoints();
        }

        /**
         * Sets points into this series. This method removes all of its points
         * and then add points specified in the method argument. If the argument
         * is null then no actions are taken.
         *
         * @param points
         *            the collection of points to set into this series.
         * @param points
         */
        public void setSeriesPoints(LinkedHashSet<DateTimePoint> points) {
            super.setPoints(points);
            updatePointXValuesIfNotPresent();
        }

        /**
         *
         */
        private void updatePointXValuesIfNotPresent() {
            double pointStart = (double) getDefPointStart();
            double pointInterval = 3600000; // 1 hour
            if (super.getConfig() instanceof BaseLineConfig) {
                BaseLineConfig config = (BaseLineConfig) super.getConfig();
                if (config.getPointStart() != null) {
                    pointStart = config.getPointStart();
                }
                if (config.getPointInterval() != null) {
                    pointInterval = config.getPointInterval();
                }
            }
            Date prevDate = new Date((long) pointStart);
            int count = 0;
            for (DateTimePoint point : getPoints()) {
                if ((point.getX() == null || (point.getX() != null && point
                        .isAutosetX()))) {
                    point.setAutosetX(true);
                    if (count == 0) {
                        point.setX(prevDate);
                        count++;
                    } else {
                        point.setX(getUpdatedDate(prevDate,
                                (long) pointInterval));
                        prevDate = point.getX();
                    }
                }
            }
        }

        private long getDefPointStart() {
            Calendar cal = GregorianCalendar.getInstance();
            cal.set(Calendar.YEAR, 1970);
            cal.set(Calendar.MONTH, 0);
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            return cal.getTimeInMillis();
        }

        private Date getUpdatedDate(Date dt, long milliseconds) {
            Calendar cal = Calendar.getInstance();
            cal.setTimeInMillis(dt.getTime() + milliseconds);
            return cal.getTime();
        }

        @Override
        public String toString() {
            return "DateTimeSeries [includeTime=" + includeTime
                    + ", getConfig()=" + getConfig() + ", getName()="
                    + getName() + ", getType()=" + getType() + ", getStack()="
                    + getStack() + ", getXAxis()=" + getXAxis()
                    + ", getYAxis()=" + getYAxis() + "]";
        }

    }

    // *******************************************************************//
    // *************** Highcharts Configuration options ******************//
    // *******************************************************************//

    public static enum SeriesType {
        COMMONSERIES("series"), LINE("line"), SPLINE("spline"), SCATTER(
                "scatter"), AREA("area"), AREASPLINE("areaspline"), BAR("bar"), COLUMN(
                "column"), PIE("pie");
        private String type;

        private SeriesType(String type) {
            this.type = type;
        }

        public String getName() {
            return this.type;
        }
    }

    static class SeriesCUR implements Serializable {
        private SeriesCURType type;
        private String name;

        public SeriesCURType getType() {
            return type;
        }

        public String getName() {
            return name;
        }

        public SeriesCUR(SeriesCURType type, String name) {
            super();
            this.type = type;
            this.name = name;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            result = prime * result + ((type == null) ? 0 : type.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            SeriesCUR other = (SeriesCUR) obj;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            if (type == null) {
                if (other.type != null)
                    return false;
            } else if (!type.equals(other.type))
                return false;
            return true;
        }

        @Override
        public String toString() {
            return "SeriesCUR [type=" + type + ", name=" + name + "]";
        }

        static enum SeriesCURType {
            ADD("Add"), UPDATE("Update"), REMOVE("Remove");
            private String name;

            private SeriesCURType(String name) {
                this.name = name;
            }

            public String getName() {
                return this.name;
            }
        }
    }

    private LinkedHashSet<SeriesCUR> seriesCURSet = new LinkedHashSet<InvientCharts.SeriesCUR>();

    boolean addSeriesCUROperation(SeriesCUR newSeriesCUR) {
        if (seriesCURSet.contains(newSeriesCUR)) {
            return false;
        }
        Iterator<SeriesCUR> seriesCURItr = seriesCURSet.iterator();
        while (seriesCURItr.hasNext()) {
            SeriesCUR seriesCUR = seriesCURItr.next();
            if (seriesCUR.getName().equals(newSeriesCUR.getName())) {
                if (SeriesCURType.REMOVE.equals(newSeriesCUR.getType())
                        && SeriesCURType.ADD.equals(seriesCUR.getType())) {
                    // Remove addition of a series as there is no reason to add
                    // a series and
                    // then remove it. E.g. If a new series is added and then
                    // removed then
                    // actually there is nothing to be done
                    seriesCURItr.remove();
                    return false;
                }
                if (SeriesCURType.UPDATE.equals(newSeriesCUR.getType())
                        && SeriesCURType.ADD.equals(seriesCUR.getType())) {
                    // There is no need for update as adding a series will
                    // take care of applying any update to the series attributes
                    // specifically visibility
                    return false;
                }
                if (SeriesCURType.REMOVE.equals(newSeriesCUR.getType())
                        && SeriesCURType.UPDATE.equals(seriesCUR.getType())) {
                    // Remove update of a series as there is no reason to update
                    // a series
                    // and then remove it. E.g. If an existing series was
                    // updated (for show/hide) and
                    // then removed then series need not be updated after all it
                    // is going to be
                    // removed. Hover, the remove operation must be captured.
                    seriesCURItr.remove();
                    break;
                }
            }
        }
        seriesCURSet.add(newSeriesCUR);
        return true;
    }

    /**
     * After a series is added or removed, there is no need to call this method
     * as it is handled implicitly. This method will send updates to the client.
     * This method should be called after adding/removing plotbands and
     * plotlines. This inconsistency will be fixed in next revision.
     *
     */
    public void refresh() {
        super.requestRepaint();
    }

    /**
     * @param arg
     */
    public static void main(String arg[]) {
        InvientChartsConfig config = new InvientChartsConfig();
        InvientCharts charts = new InvientCharts(config);
        XYSeries series = new XYSeries("s1");
        charts.addSeries(series);
        charts.addSeries(new XYSeries("s2"));
        charts.addSeries(new XYSeries("s3"));
        charts.removeSeries("s2");
        charts.addSeries(new XYSeries("s2"));
        charts.seriesCURSet.clear();
        series.show();
        charts.addSeries(series = new XYSeries("s4"));
        charts.addSeries(new XYSeries("s5"));
        series.hide();
        System.out.println(charts.seriesCURSet);
       
    }
}
TOP

Related Classes of com.invient.vaadin.charts.InvientCharts$ChartZoomListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
csObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');