Java tutorial
/* * 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.*; import java.util.concurrent.TimeUnit; import com.invient.vaadin.charts.InvientCharts.SeriesCUR.SeriesCURType; import com.invient.vaadin.charts.InvientChartsConfig.*; import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.PlotBand; import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.PlotLine; import com.invient.vaadin.charts.widgetset.client.ui.VInvientCharts; 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; /** * A Vaddin component representing charts. It is a the main class of * InvientCharts library. * <p/> * 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. * <p/> * After a chart {@link InvientCharts} is created, the following changes to the * chart will be reflected rendered on the webkit. * <ul> * <li>Set or update chart {@link Title} and/or {@link SubTitle}</li> * <li>Modify chart size</li> * <li>Add, update and remove one or more instances 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 remove one or more instances of {@link Series}</li> * <li>Show or hide one or more instances of {@link Series}</li> * <li>Add and remove one or more instances of {@link Point}</li> * <li>Register and unregister event listeners</li> * </ul> * * @author Invient */ @SuppressWarnings("serial") @ClientWidget(VInvientCharts.class) public class InvientCharts extends AbstractComponent { private InvientChartsConfig chartConfig; private boolean isRetrieveSVG = false; private boolean isPrint = false; /** * Creates this chart object with given chart configuration * * @param chartConfig The chart configuration. */ public InvientCharts(InvientChartsConfig chartConfig) { if (chartConfig == null) { throw new IllegalArgumentException("The chart cannot be created without chartConfig argument."); } this.chartConfig = chartConfig; this.chartConfig.setInvientCharts(this); } /** * Returns chart configuration object * * @return Returns chart configuration object */ public InvientChartsConfig getConfig() { return this.chartConfig; } /** * {@inheritDoc} */ @Override public void paintContent(PaintTarget target) throws PaintException { super.paintContent(target); // Update all series with reference of x and y axis. this.setAxisInAllSeriesIfNotSetAlready(); // 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(), chartConfig); InvientChartsUtil.writeYAxes(target, chartConfig.getYAxes(), chartConfig); 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"); // A flag to indicate whether to retrieve svg from client or not. target.addAttribute("isRetrieveSVG", isRetrieveSVG); // A flag to indicate whether to prompt print dialog on client or not target.addAttribute("isPrint", isPrint); // 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, seriesCURMap); } target.endTag("chartDataUpdates"); // reset flag reloadChartSeries = false; // reset series operations seriesCURMap.clear(); // reset to not retrieve svg when other updates on the chart occurs. // The svg is retrieved only when a svg available listener is registered // on this chart isRetrieveSVG = false; isPrint = false; } 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("chartSVGAvailable")) { fireChartSVGAvailable((String) eventData.get("svg")); } 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()); // TODO: [S73417H] There is something very wrong here with instanceof checks and equality. if (series != null) { if (series instanceof XYSeries) { XYSeries xySeries = (XYSeries) series; for (DecimalPoint point : xySeries.getPoints()) { if (point.getY() != null && point.getY().compareTo(eventData.getPointY()) == 0 && point.getX() != null && point.getX().compareTo(eventData.getPointX()) == 0) { return point; } } } else { DateTimeSeries dateTimeSeries = (DateTimeSeries) series; for (DateTimePoint point : dateTimeSeries.getPoints()) { if (point.getY() != null && point.getY().compareTo(eventData.getPointY()) == 0 && point.getX() != null && getDateInMilliseconds(point.getX(), ((DateTimeSeries) series).isIncludeTime()) == (long) eventData.getPointX()) { return point; } } } } return null; } private static Long getDateInMilliseconds(Date dt, boolean isIncludeTime) { if (dt == null) { return null; } if (!isIncludeTime) { return dt.getTime() - dt.getTime() % TimeUnit.DAYS.toMillis(1); } else { return dt.getTime(); } } 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 fireChartSVGAvailable(String svg) { fireEvent(new ChartSVGAvailableEvent(this, this, svg)); } 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. * <p/> * 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; } /** * @return Returns x position of mouse when a click event occurred, in * pixel */ public int getMouseX() { return mouseX; } /** * @return Returns y position of mouse when a click event occurred, in * pixel */ 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; } /** * @return Returns a category to which point is associated in case of * categorized axis only. */ public String getCategory() { return category; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return chart; } /** * @return Returns the point on which the click event occurred */ public Point getPoint() { return this.point; } /** * @return Returns the position of a mouse when the click event occurred */ 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. * <p/> * 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; } /** * @return Returns a category to which point is associated in case of * categorized axis only. */ public String getCategory() { return category; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return chart; } /** * @return Returns the point which has been removed */ 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); } /** * Point 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; } /** * @return Returns a category to which point is associated in case of * categorized axis only. */ public String getCategory() { return category; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return chart; } /** * @return Returns the unselected point */ 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; } /** * @return Returns a category to which point is associated in case of * categorized axis only. */ public String getCategory() { return category; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return chart; } /** * @return Returns the selected point */ 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; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return this.chart; } /** * @return Returns the series object on which the click event occurred */ public Series getSeries() { return this.series; } /** * @return Returns the point of a series closest to the position where * mouse click event occurred. */ public Point getNearestPoint() { return this.point; } /** * @return Returns the position of a mouse when the click event occurred */ 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; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return this.chart; } /** * @return Returns the series which got hidden */ 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; } /** * @return Returns the chart object associated with the series */ public InvientCharts getChart() { return this.chart; } /** * @return Returns the series which got displayed */ 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); } /** * Series legend item click event. This event is thrown, when legend item is * clicked. This event is not applicable for PieChart instead use {@link PieChartLegendItemClickEvent}. * * @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; } /** * @return Returns the chart object associated with the series */ public InvientCharts getChart() { return this.chart; } /** * @return Returns the series associated with the legend item */ 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; } /** * @return Returns the chart object associated with the point */ public InvientCharts getChart() { return this.chart; } /** * @return Returns the pie point (slice) associated with the legend item */ 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. * * @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. * * @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 Returns the chart object on which the click event occurred * @see InvientCharts */ public InvientCharts getChart() { return this.chart; } /** * Returns the point representing the position where the click event * occurred in axes units * * @return Returns the point representing the position where the click * event occurred in axes units * @see Point */ public Point getPoint() { return this.point; } /** * Returns the position of a mouse when the click event occurred * * @return Returns the position of a mouse when the click event occurred * @see MousePosition */ 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. * * @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. * * @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 Returns the chart object to which a series has been added. * @see InvientCharts */ 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. * * @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. * * @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 Returns the chart object for which the zoom event has * occurred */ public InvientCharts getChart() { return this.chart; } /** * Returns the chartArea object containing dimensions of zoomed area of * the chart * * @return Returns the chartArea object containing dimensions of zoomed * area of the chart */ 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. * * @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. * * @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 Returns the chart object for which zoom has been reset to * normal */ 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. * * @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. * * @param listener the listener to be removed */ public void removeListener(ChartResetZoomListener listener) { chartResetZoomListener.remove(listener); removeListener(ChartResetZoomEvent.class, listener, CHART_RESET_ZOOM_METHOD); } /** * Chart SVG event. This event is thrown, when an SVG string representing * the chart is received or ready. * <p/> * Note that this event is thrown only once after a * {@link ChartSVGAvailableListener} is registered. * * @author Invient */ public class ChartSVGAvailableEvent extends Component.Event { private InvientCharts chart; private String svg; /** * New instance of the chart svg available event. * * @param source the chart object itself * @param chart the chart object itself * @param svg an svg string representing the chart object */ public ChartSVGAvailableEvent(Component source, InvientCharts chart, String svg) { super(source); this.chart = chart; this.svg = svg; } /** * Returns the chart object for which an svg string representation is * available * * @return Returns the chart object for which an svg string * representation is available */ public InvientCharts getChart() { return this.chart; } /** * @return Returns an SVG string representing the chart */ public String getSVG() { return this.svg; } } /** * Interface for listening for a {@link ChartSVGAvailableEvent} triggered by * {@link InvientCharts}. * <p/> * The chart can have only one listener of this type registered at any time. * If a listener has already been registered and an attempt is made to * register another listener then the previously registered listener will be * unregistered and the new listener will be registered. * <p/> * A listener will be called only once after it has been registered though * it will be called again if the same listener is registered again. * * @author Invient */ public interface ChartSVGAvailableListener extends Serializable { public void svgAvailable(ChartSVGAvailableEvent chartSVGAvailableEvent); } private ChartSVGAvailableListener svgAvailableListener; /** * Adds the chart svg available listener for this chart. If the chart * already has a listener of this type then the existing listener will be * removed and the argument listener will be registered. * * @param listener the Listener to be added or registered. */ public void addListener(ChartSVGAvailableListener listener) { if (svgAvailableListener != null && svgAvailableListener != listener) { // remove earlier listener removeListener(svgAvailableListener); } svgAvailableListener = listener; addListener(ChartSVGAvailableEvent.class, svgAvailableListener, CHART_SVG_AVAILABLE_METHOD); isRetrieveSVG = true; requestRepaint(); } /** * Removes the chart svg available listener for this chart. * * @param listener the listener to be removed or unregistered. */ public void removeListener(ChartSVGAvailableListener listener) { if (svgAvailableListener == listener) { removeListener(ChartSVGAvailableEvent.class, listener, CHART_SVG_AVAILABLE_METHOD); isRetrieveSVG = false; svgAvailableListener = null; } } 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; private static final Method CHART_SVG_AVAILABLE_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 }); CHART_SVG_AVAILABLE_METHOD = ChartSVGAvailableListener.class.getDeclaredMethod("svgAvailable", new Class[] { ChartSVGAvailableEvent.class }); } catch (final java.lang.NoSuchMethodException e) { // This should never happen unless there is a typo! 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.seriesCURMap.clear(); for (Series seriesData : series) { addSeries(seriesData); } } } /** * Returns a series whose name matches the argument name. * * @param name the name of the series * @return Returns a series with the given name */ public Series getSeries(String name) { for (Series series : this.chartSeries) { if (series.getName().equals(name)) { return series; } } return null; } /** * Returns all series associated with this chart. * * @return returns all series associated with this chart. */ public LinkedHashSet<Series> getAllSeries() { return this.chartSeries; } /** * Adds the argument series to this chart. * * @param seriesData the series to be added */ public void addSeries(Series seriesData) { if (this.chartSeries.add(seriesData)) { this.setAxisInSeriesIfNotSetAlready(seriesData); seriesData.setInvientCharts(this); addSeriesCUROperation(new SeriesCUR(SeriesCURType.ADD, seriesData.getName())); requestRepaint(); } } // Before sending data to the client, this method sets // axis in all series associated with the chart private void setAxisInAllSeriesIfNotSetAlready() { for (Series series : this.chartSeries) { setAxisInSeriesIfNotSetAlready(series); } } private void setAxisInSeriesIfNotSetAlready(Series series) { if (this.getConfig() != null) { if (series.getXAxis() == null && this.getConfig().getXAxes() != null && this.getConfig().getXAxes().size() > 0) { series.setXAxis(this.getConfig().getXAxes().iterator().next()); } if (series.getYAxis() == null && this.getConfig().getYAxes() != null && this.getConfig().getYAxes().size() > 0) { series.setYAxis(this.getConfig().getYAxes().iterator().next()); } } } /** * 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. * <p/> * 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. * <p/> * 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. * <p/> * 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; private boolean shift; /** * Creates a point with given arguments. * * @param series The series to which the point must be associated. * @throws 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 * @throws 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 * @throws 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 * @throws 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; } /** * @return Returns name of this point */ public String getName() { return name; } /** * Sets name of this point * * @param name name of this point */ public void setName(String name) { this.name = name; } /** * @return Returns {@link Series} associated with this point */ public Series getSeries() { return series; } /** * @return Returns {@link PointConfig} for this point */ public PointConfig getConfig() { return config; } /** * Sets {@link PointConfig} for this point * * @param config configuration of this point * @see PointConfig */ public void setConfig(PointConfig config) { this.config = config; } /** * @return Returns true if X value of this point is set programmatically */ 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; } /** * @return Returns true if a point at the start of the series should be * shifted off when this point is appended otherwise false. */ boolean isShift() { return shift; } /** * A value of true means one point is shifted off the start of the * series as one is appended to the end. * * @param shift */ void setShift(boolean shift) { this.shift = shift; } /** * @return Returns X value of this point */ public abstract Object getX(); /** * @return Returns Y value of this point */ 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 the x value of this point * @param y the y value of this point */ 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 */ 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; } /** * @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 */ private 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 */ private 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() : "") + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((y == null) ? 0 : y.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; DecimalPoint other = (DecimalPoint) obj; // If x is null then return always false as x is calculated if not // specified if (x == null || other.x == null) { return false; } if (!x.equals(other.x)) return false; if (y == null) { if (other.y != null) return false; } else if (other.y == null) { return false; } else if (y.compareTo(other.y) != 0) return false; return true; } } /** * 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 */ private 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 */ private void setY(Double y) { this.y = y; } @Override public String toString() { return "DateTimePoint [x=" + getDateInMilliseconds(x, (getSeries() != null ? ((DateTimeSeries) getSeries()).isIncludeTime() : false)) + ", y=" + y + ", id=" + getId() + ", name=" + getName() + ", seriesName=" + (getSeries() != null ? getSeries().getName() : "") + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((y == null) ? 0 : y.hashCode()); result = prime * result + ((x == null) ? 0 : (int) x.getTime()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DateTimePoint other = (DateTimePoint) obj; // If x is null then return always false as x is calculated if not // specified if (x == null || other.x == null) { return false; } boolean pointIncludeTime = (this.getSeries() instanceof DateTimeSeries ? ((DateTimeSeries) this.getSeries()).isIncludeTime() : false); boolean pointOtherIncludeTime = (other.getSeries() instanceof DateTimeSeries ? ((DateTimeSeries) other.getSeries()).isIncludeTime() : false); Long pointX = getDateInMilliseconds(x, pointIncludeTime); Long pointOtherX = getDateInMilliseconds(other.x, pointOtherIncludeTime); if (pointX.compareTo(pointOtherX) != 0) return false; if (y == null) { if (other.y != null) return false; } else if (other.y == null) { return false; } else if (y.compareTo(other.y) != 0) return false; return true; } } /** * 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}. * <p/> * 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. * <p/> * 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. * <p/> * It is also possible to specify configuration for individual series and * not just series type. * * @author Invient */ public static abstract class Series<T extends Point> implements Serializable { private LinkedHashSet<T> points = new LinkedHashSet<T>(); 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 Returns the configuration object associated with this series */ public SeriesConfig getConfig() { return config; } /** * @return Returns name of this series */ 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 Returns stack of this series */ 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 Returns x-axis associated with this series. * @see Axis */ public XAxis getXAxis() { return xAxis; } /** * Sets x-axis of this series. A series can be associated with at most * one x-axis. * * @param xAxis */ public void setXAxis(XAxis xAxis) { this.xAxis = xAxis; } /** * @return Returns y-axis of this series. */ public YAxis getYAxis() { return yAxis; } /** * Sets y-axis of this series. A series can be associated with at most * one y-axis. * * @param yAxis */ public void setYAxis(YAxis yAxis) { this.yAxis = yAxis; } /** * @param points */ protected void removePoint(Point... points) { List<Point> pointsRemovedList = new ArrayList<Point>(); for (Point point : points) { if (this.points.remove(point)) { pointsRemovedList.add(point); } } updatePointXValuesIfNotPresent(); for (Point point : pointsRemovedList) { if (this.invientCharts != null) { this.invientCharts.addSeriesPointRemovedOperation(point.getSeries().getName(), point); this.invientCharts.requestRepaint(); } } } /** * Removes all points in this series */ protected void removeAllPoints() { this.points.clear(); if (this.invientCharts != null) { this.invientCharts.addSeriesCUROperation(new SeriesCUR(SeriesCURType.UPDATE, this.getName(), true)); this.invientCharts.requestRepaint(); } } /** * Adds one or more points into this series, specified as an argument to * this method * * @param points * @return Returns null if the argument is null otherwise returns a * collection of points which are added in this series. If a * point has same (x, y) value as any other point in the * argument points then it will not be added. */ protected LinkedHashSet<T> addPoint(boolean shift, T... points) { if (shift) { // Remove first point as other points gets appended at the end Iterator<T> pointsItr = this.points.iterator(); if (pointsItr.hasNext()) { pointsItr.next(); pointsItr.remove(); } } List<T> pointsAddedList = new ArrayList<T>(); for (T point : points) { if (this.points.add(point)) { pointsAddedList.add(point); } } updatePointXValuesIfNotPresent(); // Now record point add event as we need to know x value of a point for (Point point : pointsAddedList) { if (this.invientCharts != null) { this.invientCharts.addSeriesPointAddedOperation(point.getSeries().getName(), point); this.invientCharts.requestRepaint(); } } return new LinkedHashSet<T>(pointsAddedList); } private void addPointsInternal(LinkedHashSet<T> points) { for (T point : points) { this.points.add(point); } } /** * @return Returns all points of this series. Adding or removing any * point to or from the returned collection will not impact the * chart. To add a point or points, use addPoint() or * removePoint() method. */ protected LinkedHashSet<T> getPoints() { return new LinkedHashSet<T>(this.points); } /** * Sets points into this series * * @param points The points to set. * @return Returns {@code null} if the argument is {@code null} otherwise returns a * collection of points which are set in this series. If a point * has same (x, y) value as any other point in the argument * points then it will not be added. */ protected LinkedHashSet<T> setPoints(LinkedHashSet<T> points) { if (points != null) { this.points.clear(); addPointsInternal(points); updatePointXValuesIfNotPresent(); if (this.invientCharts != null) { this.invientCharts .addSeriesCUROperation(new SeriesCUR(SeriesCURType.UPDATE, this.getName(), true)); this.invientCharts.requestRepaint(); } return getPoints(); } return null; } /** * Each of the subclass needs to implement this method to ensure that * each point has appropriate X value even if it is not specified. */ protected abstract void updatePointXValuesIfNotPresent(); /** * Show this series */ public void show() { this.config = (this.config == null ? new SeriesConfig() : this.config); this.config.setVisible(true); if (this.invientCharts != null) { 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); if (this.invientCharts != null) { 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<DecimalPoint> { /** * 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 Points to be removed. */ public void removePoint(DecimalPoint... points) { super.removePoint(points); } /* * (non-Javadoc) * * @see com.invient.vaadin.chart.InvientChart.Series#removeAllPoints() */ public void removeAllPoints() { super.removeAllPoints(); } /** * Appends the specified point into the series if they do not exists in * this series. The points which already exists will not be appended. A * collection of points appended to this series will be returned. * * @param points Points to be added. * @return Returns a collection of points which are added in this * series. If a point has same (x, y) value as any other point * in the input argument points then it will not be added in * this series. */ public LinkedHashSet<DecimalPoint> addPoint(DecimalPoint... points) { return super.addPoint(false, points); } /** * Append the specified point into this series. If the argument shift is * true then one point is shifted off the start of this series as one is * appended to the end. * * @param point Point to add. * @param shift If true then one point is shifted off the start of this * series as one is appended to the end. * @return Returns a collection of points which are added in this * series. If a point has same (x, y) value as any other point * in the input argument points then it will not be added in * this series. */ public LinkedHashSet<DecimalPoint> addPoint(DecimalPoint point, boolean shift) { point.setShift(shift); return super.addPoint(shift, point); } /** * {@inheritDoc} */ 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. * @return Returns a collection of points which are set in this series. * If a point has same (x, y) value as any other point in the * argument points then it will not be added. */ public LinkedHashSet<DecimalPoint> setSeriesPoints(LinkedHashSet<DecimalPoint> points) { return super.setPoints(points); } @Override protected 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 (Point point : getPoints()) { if ((point.getX() == null || (point.getX() != null && point.isAutosetX()))) { DecimalPoint decimalPoint = (DecimalPoint) point; point.setAutosetX(true); if (count == 0) { decimalPoint.setX(pointStart); count++; } else { pointStart = pointStart + pointInterval; decimalPoint.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} * <p/> * By default, the time of a day is not included in the X value. In order to * include time, use a constructor with argument isIncludeTime and pass true * value for the argument. * * @author Invient * @see XYSeries */ public static class DateTimeSeries extends Series<DateTimePoint> { private boolean includeTime; /** * Creates a series with given name. This series will not consider time * in the X property of {@link DateTimePoint}. To include time, use any * constructor having isIncludeTime as part of the arguments. * * @param name the name of this series */ public DateTimeSeries(String name) { this(name, false); } /** * Creates a series with given name and boolean value. * * @param name the name of this series * @param isIncludeTime If true then the time in the X property of * {@link DateTimePoint} will be considered when drawing the * chart. Defaults to false. */ public DateTimeSeries(String name, boolean isIncludeTime) { super(name); this.includeTime = isIncludeTime; } /** * 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) { this(name, config, false); } /** * Creates a series with given name, configuration and boolean value. * * @param name the name of this series * @param config the configuration for this series * @param isIncludeTime If true then the time in the X property of * {@link DateTimePoint} will be considered when drawing the * chart. Defaults to false. */ public DateTimeSeries(String name, SeriesConfig config, boolean isIncludeTime) { super(name, config); this.includeTime = isIncludeTime; } /** * 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) { this(name, seriesType, false); } /** * Creates a series with given name, type and boolean value. * * @param name the name of this series * @param seriesType the type of this series * @param isIncludeTime If true then the time in the X property of * {@link DateTimePoint} will be considered when drawing the * chart. Defaults to false. */ public DateTimeSeries(String name, SeriesType seriesType, boolean isIncludeTime) { super(name, seriesType); this.includeTime = isIncludeTime; } /** * 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) { this(name, seriesType, config, false); } /** * Creates a series with given name, type, configuration and boolean * value. * * @param name the name of this series * @param seriesType the type of this series * @param config the configuration for this series * @param isIncludeTime If true then the time in the X property of * {@link DateTimePoint} will be considered when drawing the * chart. Defaults to false. */ public DateTimeSeries(String name, SeriesType seriesType, SeriesConfig config, boolean isIncludeTime) { super(name, seriesType, config); this.includeTime = isIncludeTime; } /** * Removes all points specified as method argument into this series * * @param points The points to be removed. */ public void removePoint(DateTimePoint... points) { super.removePoint(points); } /* * (non-Javadoc) * * @see com.invient.vaadin.chart.InvientChart.Series#removeAllPoints() */ public void removeAllPoints() { super.removeAllPoints(); } /** * Appends the specified point into the series if they do not exists in * this series. The points which already exists will not be appended. A * collection of points appended to this series will be returned. * * @param points The points to be removed. * * @return Returns a collection of points which are added in this * series. If a point has same (x, y) value as any other point * in the input argument points then it will not be added in * this series. */ public LinkedHashSet<DateTimePoint> addPoint(DateTimePoint... points) { return super.addPoint(false, points); } /** * Append the specified point into this series. If the argument shift is * true then one point is shifted off the start of this series as one is * appended to the end. * * @param point A point to be added at the end of this series * @param shift If true then one point is shifted off the start of this * series as one is appended to the end. * @return Returns a collection of points which are added in this * series. If a point has same (x, y) value as any other point * in the input argument points then it will not be added in * this series. */ public LinkedHashSet<DateTimePoint> addPoint(DateTimePoint point, boolean shift) { point.setShift(shift); return super.addPoint(shift, point); } /** * @return Returns true if the time in the X property of * {@link DateTimePoint} will be considered when drawing the * chart otherwise false. */ public boolean isIncludeTime() { return includeTime; } /** * {@inheritDoc} */ 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. * @return Returns a collection of points which are added in this * series. If a point has same (x, y) value as any other point * in the input argument points then it will not be added in * this series. */ public LinkedHashSet<DateTimePoint> setSeriesPoints(LinkedHashSet<DateTimePoint> points) { return super.setPoints(points); } @Override protected 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 (Point point : getPoints()) { DateTimePoint dateTimePoint = (DateTimePoint) point; if ((point.getX() == null || (point.getX() != null && point.isAutosetX()))) { point.setAutosetX(true); if (count == 0) { dateTimePoint.setX(prevDate); count++; } else { dateTimePoint.setX(getUpdatedDate(prevDate, (long) pointInterval)); prevDate = dateTimePoint.getX(); } } } } private static long getDefPointStart() { Calendar cal = GregorianCalendar.getInstance(); cal.set(Calendar.YEAR, 1970); cal.set(Calendar.MONTH, Calendar.JANUARY); 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 static 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() + "]"; } } 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; private boolean reloadPoints = false; private LinkedHashSet<Point> pointsAdded = new LinkedHashSet<InvientCharts.Point>(); private LinkedHashSet<Point> pointsRemoved = new LinkedHashSet<InvientCharts.Point>(); public SeriesCURType getType() { return type; } public String getName() { return name; } public SeriesCUR(SeriesCURType type, String name) { super(); this.type = type; this.name = name; } public SeriesCUR(SeriesCURType type, String name, boolean reloadPoints) { super(); this.type = type; this.name = name; this.reloadPoints = reloadPoints; } /** * Indicates whether the client/terminal should update series by setting * all data of a series instead of adding or removing individual points * * @return Returns true if the data of the series must be reloaded * otherwise false. */ boolean isReloadPoints() { return reloadPoints; } void setReloadPoints(boolean reloadPoints) { this.reloadPoints = reloadPoints; } void trackPointAdded(Point point) { pointsAdded.add(point); } void trackPointRemoved(Point point) { // If the point was added earlier and now removed // then there is no need to record its add/remove operation // as add of a point is nullified by remove of a point if (!removePointIfTrackedAlready(point)) { pointsRemoved.add(point); } } boolean removePointIfTrackedAlready(Point point) { return pointsAdded.remove(point); } // Used to clear all points added/removed when // series data is set/cleared using series.setPoints() or // series.removeAllPoints() void clearTrackedPoints() { pointsAdded.clear(); pointsRemoved.clear(); } public LinkedHashSet<Point> getPointsAdded() { return pointsAdded; } public LinkedHashSet<Point> getPointsRemoved() { return pointsRemoved; } @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 + ", reloadPoints=" + reloadPoints + ", pointsAdded=" + pointsAdded + ", pointsRemoved=" + pointsRemoved + "]"; } 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 LinkedHashMap<String, LinkedHashSet<SeriesCUR>> seriesCURMap = new LinkedHashMap<String, LinkedHashSet<SeriesCUR>>(); boolean addSeriesCUROperation(SeriesCUR newSeriesCUR) { // if (seriesCURMap.keySet().contains(newSeriesCUR.getName())) { LinkedHashSet<SeriesCUR> seriesCURSet = seriesCURMap.get(newSeriesCUR.getName()); // If for a series, no operation is found if (seriesCURSet == null || seriesCURSet.size() == 0) { seriesCURSet = new LinkedHashSet<InvientCharts.SeriesCUR>(); seriesCURSet.add(newSeriesCUR); seriesCURMap.put(newSeriesCUR.getName(), seriesCURSet); } else if (seriesCURSet.contains(newSeriesCUR)) { SeriesCUR seriesCUR = getMatchedSeriesCUR(seriesCURSet, newSeriesCUR); // In case of series update (due to series.show/hide or // series.setPoints or series.removeAllPoints) // we need to check if all points of a series are set afresh. If // so then // set a flag to indicate that instead of adding and removing // points to and from series, set series data in full. if (seriesCUR.getType().equals(SeriesCURType.UPDATE)) { seriesCUR.setReloadPoints(newSeriesCUR.isReloadPoints()); if (newSeriesCUR.isReloadPoints()) { seriesCUR.clearTrackedPoints(); } return true; } // Operation on a series has already been recorded return false; } else { 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; } else { LinkedHashSet<SeriesCUR> seriesCURSet = new LinkedHashSet<InvientCharts.SeriesCUR>(); seriesCURSet.add(newSeriesCUR); seriesCURMap.put(newSeriesCUR.getName(), seriesCURSet); return true; } } void addSeriesPointAddedOperation(String seriesName, Point point) { LinkedHashSet<SeriesCUR> seriesCURSet = seriesCURMap.get(seriesName); if (seriesCURSet == null || seriesCURSet.size() == 0) { SeriesCUR seriesCUR = new SeriesCUR(SeriesCURType.UPDATE, seriesName); seriesCUR.trackPointAdded(point); seriesCURSet = new LinkedHashSet<InvientCharts.SeriesCUR>(); seriesCURSet.add(seriesCUR); seriesCURMap.put(seriesName, seriesCURSet); } else { SeriesCUR lastSeriesCur = getLastSeriesCUR(seriesCURSet); // Track points only if series is updated. // Tracking point is useless in following cases // 1. A new series is added : In this case, a series will be added // with all points so no need to track // 2. A series is removed : In this case, a series will be removed // and hence any point added to the series doesn't carry any // meaning. if (lastSeriesCur.getType().equals(SeriesCURType.UPDATE) && !lastSeriesCur.isReloadPoints()) { lastSeriesCur.trackPointAdded(point); } } } private SeriesCUR getLastSeriesCUR(LinkedHashSet<SeriesCUR> seriesCURSet) { if (seriesCURSet == null || seriesCURSet.size() == 0) { return null; } SeriesCUR lastSeriesCur = null; for (SeriesCUR seriesCur : seriesCURSet) { lastSeriesCur = seriesCur; } return lastSeriesCur; } private SeriesCUR getMatchedSeriesCUR(LinkedHashSet<SeriesCUR> seriesCURSet, SeriesCUR matchAgainstSeriesCUR) { for (SeriesCUR seriesCur : seriesCURSet) { if (matchAgainstSeriesCUR.equals(seriesCur)) { return seriesCur; } } return null; } void addSeriesPointRemovedOperation(String seriesName, Point point) { LinkedHashSet<SeriesCUR> seriesCURSet = seriesCURMap.get(seriesName); if (seriesCURSet == null || seriesCURSet.size() == 0) { SeriesCUR seriesCUR = new SeriesCUR(SeriesCURType.UPDATE, seriesName); seriesCUR.trackPointRemoved(point); seriesCURSet = new LinkedHashSet<InvientCharts.SeriesCUR>(); seriesCURSet.add(seriesCUR); seriesCURMap.put(seriesName, seriesCURSet); } else { SeriesCUR lastSeriesCur = getLastSeriesCUR(seriesCURSet); // Track points only if series is updated. // Tracking point is useless in following cases // 1. A new series is added : In this case, a series will be added // with all points so no need to track // 2. A series is removed : In this case, a series will be removed // and hence any point removed from the series // doesn't carry any meaning. if (lastSeriesCur.getType().equals(SeriesCURType.UPDATE) && !lastSeriesCur.isReloadPoints()) { lastSeriesCur.trackPointRemoved(point); } } } /** * 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(); } /** * Displays a Print dialog of the Webkit to print this chart. Invoking this * method causes the Webkit to hide other widgets on the screen and only * this chart widget will be visible. Also it prints this chart widget as it * is displayed. */ public void print() { isPrint = true; requestRepaint(); } }