package net.seninp.grammarviz.view;
import java.awt.BasicStroke;
import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import net.seninp.gi.logic.RuleInterval;
import net.seninp.grammarviz.controller.GrammarVizController;
import net.seninp.grammarviz.session.UserSession;
import net.seninp.jmotif.sax.TSProcessor;
import net.seninp.jmotif.sax.discord.DiscordRecord;
import net.seninp.util.StackTrace;
public class GrammarvizRuleChartPanel extends JPanel implements PropertyChangeListener {
/** Fancy serial. */
private static final long serialVersionUID = 5334407476500195779L;
/** The chart container. */
private JFreeChart chart;
/** The plot itself. */
private XYPlot plot;
/** Current chart data instance. */
private UserSession session;
private TSProcessor tp;
private GrammarVizController controller;
/**
* Constructor.
*/
public GrammarvizRuleChartPanel() {
super();
tp = new TSProcessor();
}
/**
* Adds a controler instance to get normalization value from.
*
* @param controller the controller instance.
*/
public void setController(GrammarVizController controller) {
this.controller = controller;
}
/**
* Create the chart for the original time series.
*
* @return a JFreeChart object of the chart
* @throws TSException
*/
private void chartIntervals(ArrayList<double[]> intervals) throws Exception {
// making the data
//
XYSeriesCollection collection = new XYSeriesCollection();
int counter = 0;
for (double[] series : intervals) {
collection.addSeries(toSeries(counter++, series));
}
// set the renderer
//
XYLineAndShapeRenderer xyRenderer = new XYLineAndShapeRenderer(true, false);
xyRenderer.setSeriesPaint(0, new Color(0, 0, 0));
xyRenderer.setBaseStroke(new BasicStroke(3));
// X - the time axis
//
NumberAxis timeAxis = new NumberAxis();
// Y axis
//
NumberAxis valueAxis = new NumberAxis();
// put these into collection of dots
//
this.plot = new XYPlot(collection, timeAxis, valueAxis, xyRenderer);
// enable panning
//
this.plot.setDomainPannable(true);
this.plot.setRangePannable(true);
// finally, create the chart
this.chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
// and put it on the show
ChartPanel chartPanel = new ChartPanel(this.chart);
chartPanel.setMinimumDrawWidth(0);
chartPanel.setMinimumDrawHeight(0);
chartPanel.setMaximumDrawWidth(1920);
chartPanel.setMaximumDrawHeight(1200);
chartPanel.setMouseWheelEnabled(true);
// cleanup all the content
//
this.removeAll();
// put the chart on show
//
this.add(chartPanel);
// not sure if I need this
//
this.validate();
this.repaint();
}
/**
* Converts an array to a normalized XYSeries to be digested with JFreeChart.
*
* @param index
* @param series
* @return
* @throws TSException
*/
private XYSeries toSeries(int index, double[] series) throws Exception {
double[] normalizedSubseries = tp.znorm(series, controller.getSession().normalizationThreshold);
XYSeries res = new XYSeries("series" + String.valueOf(index));
for (int i = 0; i < normalizedSubseries.length; i++) {
res.add(i, normalizedSubseries[i]);
}
return res;
}
/**
* Highlight the original time series sequences of a rule.
*
* @param index index of the rule in the sequitur table.
*/
protected void chartIntervalsForRule(ArrayList<String> newlySelectedRaw) {
try {
ArrayList<double[]> intervals = new ArrayList<double[]>();
for (String str : newlySelectedRaw) {
ArrayList<RuleInterval> arrPos = this.session.chartData
.getRulePositionsByRuleNum(Integer.valueOf(str));
for (RuleInterval saxPos : arrPos) {
intervals.add(extractInterval(saxPos.getStart(), saxPos.getEnd()));
}
}
chartIntervals(intervals);
}
catch (Exception e) {
System.err.println(StackTrace.toString(e));
}
}
/**
* Highlight the original time series sequences of a sub-sequences class.
*
* @param index index of the class in the sub-sequences class table.
*/
protected void chartIntervalsForClass(ArrayList<String> newlySelectedRaw) {
try {
ArrayList<double[]> intervals = new ArrayList<double[]>();
for (String str : newlySelectedRaw) {
ArrayList<RuleInterval> arrPos = this.session.chartData
.getSubsequencesPositionsByClassNum(Integer.valueOf(str));
for (RuleInterval saxPos : arrPos) {
intervals.add(extractInterval(saxPos.getStart(), saxPos.getEnd()));
}
}
chartIntervals(intervals);
}
catch (Exception e) {
System.err.println(StackTrace.toString(e));
}
}
/**
* Charts a subsequence for a selected row in the anomaly table.
*
* @param newlySelectedAnomalies
*/
private void chartIntervalForAnomaly(ArrayList<String> newlySelectedAnomalies) {
try {
ArrayList<double[]> intervals = new ArrayList<double[]>();
for (String str : newlySelectedAnomalies) {
DiscordRecord dr = this.session.chartData.getAnomalies().get(Integer.valueOf(str));
intervals.add(extractInterval(dr.getPosition(), dr.getPosition() + dr.getLength()));
}
chartIntervals(intervals);
}
catch (Exception e) {
System.err.println(StackTrace.toString(e));
}
}
/**
* Extracts a subsequence of the original time series.
*
* @param startPos the start position.
* @param endPos the end position.
* @return the subsequence.
* @throws Exception if error occurs.
*/
private double[] extractInterval(int startPos, int endPos) throws Exception {
if (this.session.chartData.getOriginalTimeseries().length <= (endPos - startPos)) {
return Arrays.copyOf(this.session.chartData.getOriginalTimeseries(),
this.session.chartData.getOriginalTimeseries().length);
}
return Arrays.copyOfRange(this.session.chartData.getOriginalTimeseries(), startPos, endPos);
}
/**
* Clears the chart panel of the content.
*/
public void clear() {
this.removeAll();
this.validate();
this.repaint();
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (GrammarRulesPanel.FIRING_PROPERTY.equalsIgnoreCase(evt.getPropertyName())) {
@SuppressWarnings("unchecked")
ArrayList<String> newlySelectedRaw = (ArrayList<String>) evt.getNewValue();
chartIntervalsForRule(newlySelectedRaw);
}
else if (PackedRulesPanel.FIRING_PROPERTY_PACKED.equalsIgnoreCase(evt.getPropertyName())) {
@SuppressWarnings("unchecked")
ArrayList<String> newlySelectedRaw = (ArrayList<String>) evt.getNewValue();
// chartIntervalsForRule(newlySelectedRaw);
// String newlySelectedRaw = (String) evt.getNewValue();
chartIntervalsForClass(newlySelectedRaw);
}
else if (RulesPeriodicityPanel.FIRING_PROPERTY_PERIOD.equalsIgnoreCase(evt.getPropertyName())) {
String newlySelectedRaw = (String) evt.getNewValue();
ArrayList<String> param = new ArrayList<String>(1);
param.add(newlySelectedRaw);
chartIntervalsForRule(param);
}
else if (GrammarVizAnomaliesPanel.FIRING_PROPERTY_ANOMALY
.equalsIgnoreCase(evt.getPropertyName())) {
@SuppressWarnings("unchecked")
ArrayList<String> newlySelectedRaw = (ArrayList<String>) evt.getNewValue();
chartIntervalForAnomaly(newlySelectedRaw);
}
}
public void setChartData(UserSession session) {
this.session = session;
}
}