/*
* RapidMiner
*
* Copyright (C) 2001-2014 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.new_plotter.templates;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import org.apache.commons.collections15.map.HashedMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.rapidminer.datatable.DataTable;
import com.rapidminer.gui.new_plotter.configuration.AxisParallelLineConfiguration;
import com.rapidminer.gui.new_plotter.configuration.AxisParallelLinesConfigurationListener;
import com.rapidminer.gui.new_plotter.configuration.PlotConfiguration;
import com.rapidminer.gui.new_plotter.configuration.event.AxisParallelLinesConfigurationChangeEvent;
import com.rapidminer.gui.new_plotter.configuration.event.AxisParallelLinesConfigurationChangeEvent.AxisParallelLineConfigurationsChangeType;
import com.rapidminer.gui.new_plotter.data.PlotInstance;
import com.rapidminer.gui.new_plotter.engine.jfreechart.JFreeChartPlotEngine;
import com.rapidminer.gui.new_plotter.listener.RangeAxisConfigListener;
import com.rapidminer.gui.new_plotter.listener.events.RangeAxisConfigChangeEvent;
import com.rapidminer.gui.new_plotter.listener.events.ConfigurationChangeEvent.ConfigurationChangeType;
import com.rapidminer.gui.new_plotter.templates.gui.PlotterTemplatePanel;
import com.rapidminer.gui.new_plotter.templates.style.PlotterStyleProvider;
/**
* Abstract class which all templates for the new plotters have to extend.
*
* @author Marco Boeck
*
*/
public abstract class PlotterTemplate extends Observable implements Observer {
/** the current {@link DataTable} */
protected DataTable currentDataTable;
/** list containing all {@link AxisParallelLineConfiguration} for the domain axis */
protected List<AxisParallelLineConfiguration> listOfDomainLines;
/** the map containing all {@link AxisParallelLineConfiguration} for all range axes */
protected Map<String, List<AxisParallelLineConfiguration>> rangeAxisCrosshairLinesMap;
/** the domain axis crosshair change listener */
protected AxisParallelLinesConfigurationListener domainAxisLinesListener;
/** the listener which will register range axis crosshair changes */
protected RangeAxisConfigListener rangeAxisConfigListener;
/** the {@link PlotConfiguration} for the template */
protected PlotInstance plotInstance;
/** the {@link PlotterStyleProvider} for the template */
protected PlotterStyleProvider styleProvider;
/** the {@link JFreeChartPlotEngine} instance */
protected JFreeChartPlotEngine plotEngine;
/** if true, will not update plot configuration despite changes */
protected transient boolean suspendUpdates;
/** the GUI used to display the given template */
protected transient PlotterTemplatePanel guiPanel;
public static final String TEMPLATE_ELEMENT = "template";
public static final String NAME_ELEMENT = "name";
public static final String VALUE_ATTRIBUTE = "value";
public static final String CROSSHAIR_DOMAIN_TOP_ELEMENT = "domainCrosshairs";
public static final String CROSSHAIR_DOMAIN_ELEMENT = "domainCrosshair";
public static final String CROSSHAIR_RANGE_AXIS_TOP_ELEMENT = "rangeCrosshairs";
public static final String CROSSHAIR_RANGE_AXIS_ELEMENT = "rangeCrosshair";
public static final String CROSSHAIR_RANGE_AXIS_LABEL_ATTRIBUTE = "rangeCrosshairLabel";
public static final String CROSSHAIR_VALUE_ATTRIBUTE = "CrosshairValue";
public static final String CROSSHAIR_WIDTH_ATTRIBUTE = "crosshairWidth";
public static final String CROSSHAIR_STYLE_ATTRIBUTE = "crosshairStyle";
public static final String CROSSHAIR_COLOR_B_ATTRIBUTE = "b";
public static final String CROSSHAIR_COLOR_G_ATTRIBUTE = "g";
public static final String CROSSHAIR_COLOR_R_ATTRIBUTE = "r";
public static final String CROSSHAIR_COLOR_ELEMENT = "crosshairColor";
/**
* Standard constructor.
*/
public PlotterTemplate() {
listOfDomainLines = new LinkedList<AxisParallelLineConfiguration>();
rangeAxisCrosshairLinesMap = new HashedMap<String, List<AxisParallelLineConfiguration>>(40);
domainAxisLinesListener = new AxisParallelLinesConfigurationListener() {
@Override
public void axisParallelLineConfigurationsChanged(AxisParallelLinesConfigurationChangeEvent e) {
// domain axis crosshair handling
if (e.getType().equals(AxisParallelLineConfigurationsChangeType.LINE_ADDED)) {
if (!listOfDomainLines.contains(e.getLineConfiguration())) {
listOfDomainLines.add(e.getLineConfiguration());
}
} else if (e.getType().equals(AxisParallelLineConfigurationsChangeType.LINE_REMOVED)) {
listOfDomainLines.remove(e.getLineConfiguration());
}
}
};
rangeAxisConfigListener = new RangeAxisConfigListener() {
@Override
public void rangeAxisConfigChanged(RangeAxisConfigChangeEvent e) {
if (e.getConfigurationChangeType().equals(ConfigurationChangeType.RANGE_AXIS_CONFIG_CHANGE)) {
AxisParallelLinesConfigurationChangeEvent crosshairChange = e.getCrosshairChange();
if (crosshairChange != null) {
List<AxisParallelLineConfiguration> lineList = rangeAxisCrosshairLinesMap.get(e.getSource().getLabel());
if (lineList == null) {
lineList = new LinkedList<AxisParallelLineConfiguration>();
}
if (crosshairChange.getType().equals(AxisParallelLineConfigurationsChangeType.LINE_ADDED)) {
if (!lineList.contains(crosshairChange.getLineConfiguration())) {
lineList.add(crosshairChange.getLineConfiguration());
}
} else if (crosshairChange.getType().equals(AxisParallelLineConfigurationsChangeType.LINE_REMOVED)) {
lineList.remove(crosshairChange.getLineConfiguration());
}
rangeAxisCrosshairLinesMap.put(e.getSource().getLabel(), lineList);
}
}
}
};
}
/**
* Sets the {@link PlotterStyleProvider} for this {@link PlotterTemplate}.
* @param styleProvider
*/
public void setStyleProvider(PlotterStyleProvider styleProvider) {
this.styleProvider = styleProvider;
this.styleProvider.addObserver(this);
}
/**
* Gets the {@link PlotterStyleProvider} for this {@link PlotterTemplate}.
* @return
*/
public PlotterStyleProvider getStyleProvider() {
return this.styleProvider;
}
/**
* Gets the name of the chart for the {@link PlotterTemplate}. This is the name which will
* be displayed in the chart type combobox.
* @return the chart type
*/
public abstract String getChartType();
/**
* Gets the {@link PlotterTemplatePanel} which can be used to configurate the {@link PlotterTemplate}.
* @return the configuration {@link PlotterTemplatePanel}
*/
public PlotterTemplatePanel getTemplateConfigurationPanel() {
return guiPanel;
}
/**
* Gets the {@link PlotConfiguration} object used to setup the plotter for this specific chart type.
* @return the {@link PlotConfiguration}
*/
public PlotConfiguration getPlotConfiguration() {
return plotInstance.getMasterPlotConfiguration();
}
/**
* Gets the {@link PlotInstance} object for this template.
* @return the {@link PlotInstance}
*/
public PlotInstance getPlotInstance() {
return plotInstance;
}
@Override
public String toString() {
return getChartType();
}
/**
* Set the {@link PlotInstance} object for the template.
* @param plotConfig
*/
public void setPlotInstance(PlotInstance plotInstance) {
if (plotInstance == null) {
throw new IllegalArgumentException("PlotInstance must not be null!");
}
// save crosshair lines, as we replace the plotInstance (and therefore plotConfiguration)
if (this.plotInstance != null) {
this.plotInstance.getMasterPlotConfiguration().getDomainConfigManager().getCrosshairLines().removeAxisParallelLinesConfigurationListener(domainAxisLinesListener);
}
plotInstance.getMasterPlotConfiguration().getDomainConfigManager().getCrosshairLines().addAxisParallelLinesConfigurationListener(domainAxisLinesListener);
this.plotInstance = plotInstance;
if (guiPanel != null) {
guiPanel.updatePlotInstance(plotInstance);
}
}
/**
* Call to notify the plotter template that the data has changed.
* @param dataTable the new data
*/
public synchronized void fireDataUpdated(final DataTable dataTable) {
this.currentDataTable = dataTable;
dataUpdated(dataTable);
setChanged();
notifyObservers();
}
/**
* Sets the {@link JFreeChartPlotEngine} instance.
* @param plotEngine
*/
public void setPlotEngine(JFreeChartPlotEngine plotEngine) {
this.plotEngine = plotEngine;
}
/**
* Gets the current {@link DataTable} used by this {@link PlotterTemplate}.
* @return
*/
public DataTable getDataTable() {
return currentDataTable;
}
/**
* Handles the template specific handling when the data changes.
* @param plotEngine the {@link JFreeChartPlotEngine} instance
* @param dataTable the new data
*/
protected abstract void dataUpdated(final DataTable dataTable);
/**
* Returns the I18N name of the {@link PlotterTemplate} as displayed in the GUI.
* @return the I18N name
*/
public static String getI18NName() {
throw new IllegalAccessError("method must be implemented by each template so this method will be hidden!");
}
/**
* Updates the current plot depending on the template.
*/
protected abstract void updatePlotConfiguration();
/**
* Gets the {@link JFreeChartPlotEngine} instance.
*/
public JFreeChartPlotEngine getPlotEngine() {
return plotEngine;
}
/**
* Writes all {@link PlotterTemplate} settings to the given {@link Document}.
* @param document the {@link Document} where all settings are written to
* @return the template {@link Element}
*/
public abstract Element writeToXML(Document document);
/**
* Loads all {@link PlotterTemplate} settings from the given {@link Element}.
* @param templateElement the {@link Element} where all settings are loaded from
*/
public abstract void loadFromXML(Element templateElement);
@Override
public void update(Observable o, Object arg) {
if (o instanceof PlotterStyleProvider) {
updatePlotConfiguration();
}
}
/**
* Forces an update of the chart.
*/
public void forceUpdate() {
updatePlotConfiguration();
}
}