/*
* Copyright 2007-2013
* Licensed under GNU Lesser General Public License
*
* This file is part of EpochX
*
* EpochX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EpochX 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with EpochX. If not, see <http://www.gnu.org/licenses/>.
*
* The latest version is available from: http://www.epochx.org
*/
package org.epochx.monitor.chart;
import info.monitorenter.gui.chart.Chart2D;
import info.monitorenter.gui.chart.IAxis;
import java.awt.Color;
import java.awt.Dimension;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Timer;
import javax.imageio.ImageIO;
import org.apache.xmlgraphics.java2d.ps.AbstractPSDocumentGraphics2D;
import org.apache.xmlgraphics.java2d.ps.EPSDocumentGraphics2D;
import org.apache.xmlgraphics.java2d.ps.PSDocumentGraphics2D;
import org.epochx.event.Event;
import org.epochx.monitor.MonitorUtilities;
/**
* A <code>Chart</code> is a <code>Monitor</code> component which displays one
* or more <code>ChartTrace</code>.
* <p>
* This class extends <code>Chart2D</code> from the <a
* href="http://jchart2d.sourceforge.net/">JChart2D</a> library.
* </p>
*
* <p>
* <h3>Construction</h3>
* The chart name cans be specified in the constructor. If not, a default name
* is given.<br>
* A <code>Chart</code> is composed by one or more {@link ChartTrace} instances.
* <br>
* <br>
* Here, there is a sample code which creates a <code>Chart</code>, adds a
* trace, and adds a listener for all traces :
*
* <pre>
* Chart myChart = new Chart("Chart_Name");
* ChartTrace aTrace = new ChartTrace();
* // .. Chart Trace Setting ..
* myChart.addTrace(aTrace);
* myChart.addListener(EndGeneration.class); // optional
* </pre>
*
* To know how to create and set a trace please, see {@link ChartTrace}.
* </p>
*
* <p>
* <h3>Timer use & Refreshing rate</h3>
* Each <code>Chart</code> instance has its own {@link Timer} which shedules the
* refreshing {@link ChartTrace#task task} of each trace which could have
* different refresh rates (<i>100ms</i> by default).<br>
* The refresh task is not performed if the chart is not visible on the
* <code>Monitor</code>.<br>
* <i> (Note that as we use a timer for each instance, each chart refresh in a
* separated thread.) </i>.
* </p>
*
* <p>
* <h3>Concurrency</h3>
* All the fields are <b>immutables</b>, except the private trace list.<br>
* Some methods (
* <code>addTrace(ChartTrace), clear(), getTraceCount(), ...</code>), which
* access to the list, might also appear unsafe for a multiple-threads use.<br>
* Even if this case does not seem intended, those methods are
* <b>synchronized</b> by intrinsic <i>lock</i>.
* </p>
*
* <p>
* <h3>Export</h3>
* A <code>Chart</code> can be exported with a choosable size, in raster formats
* or in vectorial formats, by using the {@link #export(File, String, Dimension)
* export} method.<br>
* <br>
* <b>Note that we do not guarantee that this method is thread-safe.</b>
* Especially, if you try to export the <code>Chart</code> during the evolution
* process as the <i>EDT</i> might refresh the <code>Chart</code> during the
* exportation. However, no conflict issues seem have occurred in our tests.
* </p>
*
*
* @see ChartTrace
* @see Timer
*/
public class Chart extends Chart2D {
/**
* Generated serial UID.
*/
private static final long serialVersionUID = -6969229302568666457L;
/**
* The raster format (<i>*.png, *.jpg, *.gif, *.bmp</i>) constant.
*/
public static final String FORMAT_RASTER = "raster";
/**
* The vector format (<i>*.ps, *.eps</i>) constant.
*/
public static final String FORMAT_VECTOR = "vector";
/**
* The number of created instances.
*/
private static int noInstances = 0;
/**
* The color table (can be extended).
*/
private final static Color[] colors = {Color.BLUE, Color.GREEN, Color.RED, Color.ORANGE, Color.CYAN, Color.MAGENTA,
Color.GRAY, Color.PINK};
/**
* The <code>Timer</code>.
*/
private final Timer timer;
/**
* The list of <code>ChartTrace</code> contained in the chart.
*/
private final ArrayList<ChartTrace> traces = new ArrayList<ChartTrace>();
/**
* Constructs a <code>Chart</code>.
*/
public Chart() {
this("Chart" + noInstances);
}
/**
* Constructs a <code>Chart</code>.
*
* @param name the name of the <code>MonitorGraph</code>.
*/
public Chart(String name) {
// Chart settings
setName(name);
setPreferredSize(new Dimension(500, 400));
// chart.setBackground(Color.BLACK);
// chart.setForeground(Color.WHITE);
setGridColor(Color.LIGHT_GRAY);
// Axis settings
IAxis<?> axisX = this.getAxisX();
axisX.setPaintGrid(true);
IAxis<?> axisY = this.getAxisY();
axisY.setPaintGrid(true);
// Timer settings
timer = new Timer("MONITOR-ChartTimer (" + name + ")");
// Number of Instances incrementation.
noInstances++;
}
/**
* Returns the <code>Timer</code>.
*
* @return the <code>Timer</code>.
*/
protected Timer getTimer() {
return timer;
}
/**
* Returns a <code>Color</code> among the table of colors.
*
* @return a <code>Color</code> among the table of colors.
*/
protected Color getColor() {
return colors[getTraceCount() % colors.length];
}
/**
* Returns the number of traces.
*
* @return the number of traces.
*/
protected synchronized int getTraceCount() {
return traces.size();
}
/**
* Adds a trace to the trace list and to the chart.
*
* @param graphTrace the trace added to the trace list.
*/
public synchronized <E extends Event> void addTrace(ChartTrace graphTrace) {
graphTrace.setChart(this);
traces.add(graphTrace);
super.addTrace(graphTrace);
}
/**
* Clears the trace list.
*/
public synchronized void clear() {
traces.clear();
}
/**
* Adds an event to the listeners list of all the traces.
*
* @param type the even added.
*/
public synchronized <E extends Event> void addListener(Class<E> type) {
if (!traces.isEmpty())
for (ChartTrace trace: traces)
trace.addListener(type);
}
/**
* Exports a <code>Chart</code>.
*
* @param file the file in which the <code>Chart</code> is exported.
* @param format the format among FORMAT_RASTER, FORMAT_VECTOR.
* @param size the size in which the graph should be printed.
* @throws IllegalArgumentException if the format is unknown.
*
* @see #FORMAT_RASTER
* @see #FORMAT_VECTOR
*/
public synchronized void export(File file, String format, Dimension size) throws IllegalArgumentException {
String extension = MonitorUtilities.getExtension(file);
if (format == FORMAT_RASTER) {
if (extension == null)
extension = "png";
try {
ImageIO.write(snapShot((int) size.getWidth(), (int) size.getHeight()), extension, file);
} catch (IOException e) {
e.printStackTrace();
}
} else if (format == FORMAT_VECTOR) {
setVisible(false);
setSize(size);
try {
FileOutputStream outStream = new FileOutputStream(file);
AbstractPSDocumentGraphics2D g2d = null;
if (extension == "eps")
g2d = new EPSDocumentGraphics2D(true);
else
g2d = new PSDocumentGraphics2D(true);
g2d.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
g2d.setupDocument(outStream, (int) size.getWidth(), (int) size.getHeight());
paint(g2d);
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
setVisible(true);
} else
throw new IllegalArgumentException("Unknown format.");
}
public String toString() {
return getName();
}
}