From: Marko Luukkainen Date: Wed, 8 Dec 2021 17:56:56 +0000 (+0200) Subject: JFreeChart implementation is leaking memory X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=2ed2eef2e82441db71c9a4f384130483ab1937cf;p=simantics%2Fsysdyn.git JFreeChart implementation is leaking memory Old Plot properties were not diposed when new Plot was set ChartComposites did not dispose active chart. Setting object references to null in Plot.dispose() implementations gitlab #86 --- diff --git a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/AbstractPlot.java b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/AbstractPlot.java index 600de403..2d5daec8 100644 --- a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/AbstractPlot.java +++ b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/AbstractPlot.java @@ -58,9 +58,15 @@ public abstract class AbstractPlot implements IPlot { for(IDataset dataset : currentProperties.datasets) dataset.dispose(); + currentProperties = null; } - if(listener != null) + if(listener != null) { listener.dispose(); + listener = null; + } + jfreechart = null; + plot = null; + } @Override @@ -83,7 +89,17 @@ public abstract class AbstractPlot implements IPlot { protected abstract Plot newPlot(); protected void setPlotProperties(PlotProperties properties) { - this.currentProperties = properties; + if (currentProperties != null) { + for (IAxis axis : currentProperties.ranges) + axis.dispose(); + + for (IAxis axis : currentProperties.domains) + axis.dispose(); + + for (IDataset dataset : currentProperties.datasets) + dataset.dispose(); + } + this.currentProperties = properties; } protected abstract void getOtherProperties(ReadGraph graph, PlotProperties properties) throws DatabaseException; diff --git a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite.java b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite.java index e9088797..53cdf1c4 100644 --- a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite.java +++ b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite.java @@ -17,6 +17,8 @@ import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.awt.SWT_AWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Composite; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; @@ -66,6 +68,14 @@ public class ChartComposite extends Composite { } catch (DatabaseException e) { e.printStackTrace(); } + + this.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + doDispose(); + } + }); } /** @@ -78,6 +88,37 @@ public class ChartComposite extends Composite { super(parent, style | SWT.NO_BACKGROUND | SWT.EMBEDDED); CreateContent(chartResource); } + + public void doDispose() { + if (chart != null) { + chart.dispose(); + chart = null; + } + } + + /** + * This query does not implement equals or hashCode by purpose. The result is not reusable. + * + * @author luukkainen + * + */ + protected static class ChartRead implements Read { + private Resource chartResource; + + public ChartRead(Resource chartResource) { + this.chartResource = chartResource; + } + + @Override + public IJFreeChart perform(ReadGraph graph) throws DatabaseException { + // Adapt chartResource to a chart (XY, pie, bar, ...) + if(graph.isInstanceOf(chartResource, JFreeChartResource.getInstance(graph).Chart)) { + return graph.adapt(chartResource, IJFreeChart.class); + } else { + return null; + } + } + } /** * Creates and displays the chart defined in chartResource @@ -91,22 +132,7 @@ public class ChartComposite extends Composite { frame = SWT_AWT.new_Frame(composite); // Add a listener displaying the contents of the chart. Chart is re-drawn if the definition changes - Simantics.getSession().asyncRequest(new Read() { - - @Override - public IJFreeChart perform(ReadGraph graph) throws DatabaseException { - // Adapt chartResource to a chart (XY, pie, bar, ...) - if(graph.isInstanceOf(chartResource, JFreeChartResource.getInstance(graph).Chart)) { - if(chart != null) - chart.dispose(); - chart = graph.adapt(chartResource, IJFreeChart.class); - return chart; - } else { - return null; - } - } - - } , new AsyncListener() { + Simantics.getSession().asyncRequest(new ChartRead(chartResource), new AsyncListener() { @Override public boolean isDisposed() { @@ -117,7 +143,9 @@ public class ChartComposite extends Composite { public void execute(AsyncReadGraph graph, IJFreeChart chart) { if(chart == null || composite.isDisposed()) return; - + if (ChartComposite.this.chart != null) + ChartComposite.this.chart.dispose(); + ChartComposite.this.chart = chart; JFreeChart jfreeChart = chart.getChart(); // Display the result chart if (composite.isDisposed()) diff --git a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite2.java b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite2.java index 32ef7c34..0efffbc3 100644 --- a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite2.java +++ b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/ChartComposite2.java @@ -113,18 +113,41 @@ public class ChartComposite2 extends SWTAWTComponent { return chart; } + @Override + public void doDispose() { + if (chart != null) { + chart.dispose(); + chart = null; + } + super.doDispose(); + } + protected Read getChartQuery(final Resource chartResource) { - return new Read() { - @Override - public IJFreeChart perform(ReadGraph graph) throws DatabaseException { - // Adapt chartResource to a chart (XY, pie, bar, ...) - if(graph.isInstanceOf(chartResource, JFreeChartResource.getInstance(graph).Chart)) { - return graph.adapt(chartResource, IJFreeChart.class); - } else { - return null; - } + return new ChartRead(chartResource); + } + + /** + * This query does not implement equals or hashCode by purpose. The result is not reusable. + * + * @author luukkainen + * + */ + protected static class ChartRead implements Read { + private Resource chartResource; + + public ChartRead(Resource chartResource) { + this.chartResource = chartResource; + } + + @Override + public IJFreeChart perform(ReadGraph graph) throws DatabaseException { + // Adapt chartResource to a chart (XY, pie, bar, ...) + if(graph.isInstanceOf(chartResource, JFreeChartResource.getInstance(graph).Chart)) { + return graph.adapt(chartResource, IJFreeChart.class); + } else { + return null; } - }; + } } /** diff --git a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/PiePlot.java b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/PiePlot.java index f06bdbea..af78ae14 100644 --- a/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/PiePlot.java +++ b/bundles/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/PiePlot.java @@ -1,173 +1,180 @@ -/******************************************************************************* - * Copyright (c) 2007, 2011 Association for Decentralized Information Management in - * Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.jfreechart.chart; - -import java.awt.Color; -import java.awt.Font; -import java.util.HashMap; - -import org.jfree.chart.labels.StandardPieSectionLabelGenerator; -import org.jfree.chart.labels.StandardPieToolTipGenerator; -import org.jfree.chart.plot.DefaultDrawingSupplier; -import org.jfree.chart.plot.Plot; -import org.jfree.data.general.Dataset; -import org.jfree.data.general.DatasetChangeEvent; -import org.jfree.data.general.DatasetChangeListener; -import org.jfree.ui.RectangleInsets; -import org.simantics.databoard.Bindings; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.exception.DatabaseException; -import org.simantics.sysdyn.JFreeChartResource; - -/** - * Class representing a PiePlot in JFreeChart ontology - * - * @author Teemu Lempinen - * - */ -public class PiePlot extends AbstractPlot { - - protected org.jfree.data.general.PieDataset pieDataset; - private DatasetChangeListener changeListener; - - public PiePlot(ReadGraph graph, Resource resource) { - super(graph, resource); - } - - /** - * Pie plot class with a stricter equals condition - * @author Teemu Lempinen - * - */ - protected static class MyPiePlot extends org.jfree.chart.plot.PiePlot { - - private static final long serialVersionUID = -5917620061541212934L; - - @Override - public boolean equals(Object obj) { - boolean result = super.equals(obj); - if(result == true) { - org.jfree.chart.plot.PiePlot that = (org.jfree.chart.plot.PiePlot) obj; - if (this.getDataset() != that.getDataset()) { - return false; // Normally plot does not check this. We need this to properly update the charts - } - } - - return result; - } - - } - - @Override - protected Plot newPlot() { - MyPiePlot plot = new MyPiePlot(); - plot.setToolTipGenerator(new StandardPieToolTipGenerator()); - return plot; - } - - @Override - protected void getOtherProperties(ReadGraph graph, PlotProperties properties) throws DatabaseException { - Boolean labelsVisible = graph.getPossibleRelatedValue(resource, JFreeChartResource.getInstance(graph).Plot_visibleLabels, Bindings.BOOLEAN); - properties.otherProperties.put("labelsVisible", labelsVisible); - - Boolean useFilter = graph.getPossibleRelatedValue(resource, JFreeChartResource.getInstance(graph).Filter_used, Bindings.BOOLEAN); - Double filterFraction = graph.getPossibleRelatedValue(resource, JFreeChartResource.getInstance(graph).Filter_fraction, Bindings.DOUBLE); - properties.otherProperties.put("useFilter", useFilter); - properties.otherProperties.put("filterFraction", filterFraction); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - protected void setPlotProperties(PlotProperties properties) { - if(!(plot instanceof org.jfree.chart.plot.PiePlot)) - return; - - final org.jfree.chart.plot.PiePlot piePlot = (org.jfree.chart.plot.PiePlot)plot; - - if(!properties.datasets.isEmpty()) { - // We assume that a pie plot has only one dataset - final IDataset ds = properties.datasets.get(0); - Dataset dataset = ((PieDataset)ds).getDataset(); - - if(dataset == null) - return; - - if(pieDataset != null && changeListener != null) { - pieDataset.removeChangeListener(changeListener); - } - - pieDataset = (org.jfree.data.general.PieDataset)dataset; - piePlot.setDataset(pieDataset); - - if (pieDataset instanceof FilteredDataset) { - FilteredDataset f = (FilteredDataset)pieDataset; - Boolean useFilter = (Boolean)properties.otherProperties.get("useFilter"); - Double filterFraction = (Double)properties.otherProperties.get("filterFraction"); - if (useFilter != null && filterFraction != null) { - f.setFiltering(useFilter); - f.setFilterFraction(filterFraction*0.01); - f.updateFiltered(); - } else { - f.setFiltering(false); - } - } - - Boolean labelsVisible = (Boolean)properties.otherProperties.get("labelsVisible"); - if(Boolean.FALSE.equals(labelsVisible)) - piePlot.setLabelGenerator(null); - else if(piePlot.getLabelGenerator() == null) - piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator()); - - changeListener = new DatasetChangeListener() { - - @Override - public void datasetChanged(DatasetChangeEvent event) { - HashMap, Color> colorMap = ((PieDataset)ds).getColorMap(); - HashMap, Boolean> explodedMap = ((PieDataset)ds).getExplodedMap(); - - for(Object o : piePlot.getDataset().getKeys()) { - if(o instanceof Comparable) { - Comparable key = (Comparable)o; - if(explodedMap.containsKey(key) && explodedMap.get(key)) { - piePlot.setExplodePercent(key, 0.3); - - } else { - piePlot.setExplodePercent(key, 0); - } - } - } - - for(Comparable name : explodedMap.keySet()) { - Boolean exploded = explodedMap.get(name); - if(Boolean.TRUE.equals(exploded)) - piePlot.setExplodePercent(name, 0.3); - } - piePlot.clearSectionPaints(false); - piePlot.setDrawingSupplier(new DefaultDrawingSupplier()); - for(Comparable name : colorMap.keySet()) - piePlot.setSectionPaint(name, colorMap.get(name)); - } - }; - - pieDataset.addChangeListener(changeListener); - } - - // Cleaner look: no outline borders - piePlot.setInsets(new RectangleInsets(0,0,0,0), false); - piePlot.setOutlineVisible(false); - piePlot.setLabelBackgroundPaint(Color.WHITE); - piePlot.setLabelFont(new Font("helvetica", Font.PLAIN, 11)); - super.setPlotProperties(properties); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.jfreechart.chart; + +import java.awt.Color; +import java.awt.Font; +import java.util.HashMap; + +import org.jfree.chart.labels.StandardPieSectionLabelGenerator; +import org.jfree.chart.labels.StandardPieToolTipGenerator; +import org.jfree.chart.plot.DefaultDrawingSupplier; +import org.jfree.chart.plot.Plot; +import org.jfree.data.general.Dataset; +import org.jfree.data.general.DatasetChangeEvent; +import org.jfree.data.general.DatasetChangeListener; +import org.jfree.ui.RectangleInsets; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.JFreeChartResource; + +/** + * Class representing a PiePlot in JFreeChart ontology + * + * @author Teemu Lempinen + * + */ +public class PiePlot extends AbstractPlot { + + protected org.jfree.data.general.PieDataset pieDataset; + private DatasetChangeListener changeListener; + + public PiePlot(ReadGraph graph, Resource resource) { + super(graph, resource); + } + + /** + * Pie plot class with a stricter equals condition + * @author Teemu Lempinen + * + */ + protected static class MyPiePlot extends org.jfree.chart.plot.PiePlot { + + private static final long serialVersionUID = -5917620061541212934L; + + @Override + public boolean equals(Object obj) { + boolean result = super.equals(obj); + if(result == true) { + org.jfree.chart.plot.PiePlot that = (org.jfree.chart.plot.PiePlot) obj; + if (this.getDataset() != that.getDataset()) { + return false; // Normally plot does not check this. We need this to properly update the charts + } + } + + return result; + } + + } + + @Override + protected Plot newPlot() { + MyPiePlot plot = new MyPiePlot(); + plot.setToolTipGenerator(new StandardPieToolTipGenerator()); + return plot; + } + + @Override + public void dispose() { + pieDataset = null; + changeListener = null; + super.dispose(); + } + + @Override + protected void getOtherProperties(ReadGraph graph, PlotProperties properties) throws DatabaseException { + Boolean labelsVisible = graph.getPossibleRelatedValue(resource, JFreeChartResource.getInstance(graph).Plot_visibleLabels, Bindings.BOOLEAN); + properties.otherProperties.put("labelsVisible", labelsVisible); + + Boolean useFilter = graph.getPossibleRelatedValue(resource, JFreeChartResource.getInstance(graph).Filter_used, Bindings.BOOLEAN); + Double filterFraction = graph.getPossibleRelatedValue(resource, JFreeChartResource.getInstance(graph).Filter_fraction, Bindings.DOUBLE); + properties.otherProperties.put("useFilter", useFilter); + properties.otherProperties.put("filterFraction", filterFraction); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + protected void setPlotProperties(PlotProperties properties) { + if(!(plot instanceof org.jfree.chart.plot.PiePlot)) + return; + + final org.jfree.chart.plot.PiePlot piePlot = (org.jfree.chart.plot.PiePlot)plot; + + if(!properties.datasets.isEmpty()) { + // We assume that a pie plot has only one dataset + final IDataset ds = properties.datasets.get(0); + Dataset dataset = ((PieDataset)ds).getDataset(); + + if(dataset == null) + return; + + if(pieDataset != null && changeListener != null) { + pieDataset.removeChangeListener(changeListener); + } + + pieDataset = (org.jfree.data.general.PieDataset)dataset; + piePlot.setDataset(pieDataset); + + if (pieDataset instanceof FilteredDataset) { + FilteredDataset f = (FilteredDataset)pieDataset; + Boolean useFilter = (Boolean)properties.otherProperties.get("useFilter"); + Double filterFraction = (Double)properties.otherProperties.get("filterFraction"); + if (useFilter != null && filterFraction != null) { + f.setFiltering(useFilter); + f.setFilterFraction(filterFraction*0.01); + f.updateFiltered(); + } else { + f.setFiltering(false); + } + } + + Boolean labelsVisible = (Boolean)properties.otherProperties.get("labelsVisible"); + if(Boolean.FALSE.equals(labelsVisible)) + piePlot.setLabelGenerator(null); + else if(piePlot.getLabelGenerator() == null) + piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator()); + + changeListener = new DatasetChangeListener() { + + @Override + public void datasetChanged(DatasetChangeEvent event) { + HashMap, Color> colorMap = ((PieDataset)ds).getColorMap(); + HashMap, Boolean> explodedMap = ((PieDataset)ds).getExplodedMap(); + + for(Object o : piePlot.getDataset().getKeys()) { + if(o instanceof Comparable) { + Comparable key = (Comparable)o; + if(explodedMap.containsKey(key) && explodedMap.get(key)) { + piePlot.setExplodePercent(key, 0.3); + + } else { + piePlot.setExplodePercent(key, 0); + } + } + } + + for(Comparable name : explodedMap.keySet()) { + Boolean exploded = explodedMap.get(name); + if(Boolean.TRUE.equals(exploded)) + piePlot.setExplodePercent(name, 0.3); + } + piePlot.clearSectionPaints(false); + piePlot.setDrawingSupplier(new DefaultDrawingSupplier()); + for(Comparable name : colorMap.keySet()) + piePlot.setSectionPaint(name, colorMap.get(name)); + } + }; + + pieDataset.addChangeListener(changeListener); + } + + // Cleaner look: no outline borders + piePlot.setInsets(new RectangleInsets(0,0,0,0), false); + piePlot.setOutlineVisible(false); + piePlot.setLabelBackgroundPaint(Color.WHITE); + piePlot.setLabelFont(new Font("helvetica", Font.PLAIN, 11)); + super.setPlotProperties(properties); + } + +}