diff --git a/src/main/java/fr/irstea/associatione/model/Model.java b/src/main/java/fr/irstea/associatione/model/Model.java index bbb0760b206f8199164af625cbb06c022a5e3f95..191c0a7242bfc3d18f6f8351a8f871eb4dbb983a 100644 --- a/src/main/java/fr/irstea/associatione/model/Model.java +++ b/src/main/java/fr/irstea/associatione/model/Model.java @@ -115,6 +115,21 @@ public class Model { } return data; } + + public double[] getSubjectiveNorm() { + double[] opinionsMeanOnSubjects = getOpinionsMeanOnSubjects(); + double[] data = new double[individuals.length]; + for (int i = 0; i < data.length; i++) { + data[i] = 0; + for (int j = 0; j < data.length; j++) { + if (i!=j) { + data[i] += opinionsMeanOnSubjects[j] * individuals[i].getOpinionOnIndividual(j); + } + } + data[i] = data[i] / (data.length - 1); + } + return data; + } public static void main(String[] args) throws ProcessingException, IllegalArgumentException, IllegalAccessException { Parameters parameters = new Parameters(0, 50, 1, new DistorsionFromOpinions(), 1, 0.3); diff --git a/src/main/java/fr/irstea/associatione/model/ReflectUtils.java b/src/main/java/fr/irstea/associatione/model/ReflectUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2f6b0e1a0ce0746a70b5107cebdf524230f4f498 --- /dev/null +++ b/src/main/java/fr/irstea/associatione/model/ReflectUtils.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 dumoulin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package fr.irstea.associatione.model; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author dumoulin + */ +public class ReflectUtils { + + private static final Logger LOGGER = Logger.getLogger(ReflectUtils.class.getName()); + + /** + * + * @param type + * @param name + * @return null if the method wasn't found + */ + public static Method getMethod(Class type, String name) throws NoSuchFieldException { + Class current = type; + while (current != null) { + for (Method m : current.getDeclaredMethods()) { + if (m.getName().equals(name)) { + return m; + } + } + current = current.getSuperclass(); + } + throw new NoSuchFieldException("The method " + name + " hasn't been found in the class " + type.getName()); + } + + /** + * + * @param method + * @param instance + * @return null if a problem has occured during the introspection. + */ + public static Object invokeMethod(Method method, Object instance) { + boolean old = method.isAccessible(); + method.setAccessible(true); + Object value = null; + try { + boolean accessible = method.isAccessible(); + method.setAccessible(true); + value = method.invoke(instance); + method.setAccessible(accessible); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + LOGGER.log(Level.SEVERE, "Error during method invocation " + method, ex); + } + method.setAccessible(old); + return value; + } + + /** + * + * @param methodname + * @param instance + * @return null if a problem has occured during the introspection. + */ + public static Object invokeMethod(String methodname, Object instance) throws NoSuchFieldException { + Object value = null; + Method method = getMethod(instance.getClass(), methodname); + if (method != null) { + value = ReflectUtils.invokeMethod(method, instance); + } + return value; + } + + /** + * + * @param instance + * @param path The path of the method to retrieve from the class of the + * instance given. The path must contain a dot "." for accessing nested + * methods. The element of a list can be accessed by its index. For example + * : <tt>elements.2.aMethod</tt> + * @return + * @throws NoSuchFieldException + */ + public static MethodOnInstance getMethodOnInstance(Object instance, String path) throws NoSuchFieldException { + if (path.contains(".")) { + String[] nodes = path.split("\\."); + for (int i = 0; i < nodes.length - 1; i++) { + if (nodes[i].matches("[0-9]*")) { + instance = ((List) instance).get(Integer.parseInt(nodes[i])); + } else { + instance = ReflectUtils.invokeMethod(nodes[i], instance); + } + } + path = nodes[nodes.length - 1]; + } + return new MethodOnInstance(instance, getMethod(instance.getClass(), path)); + } + + public static void invokeMethodFromPath(Object instance, String path) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + getMethodOnInstance(instance, path).invoke(); + } + + /** + * Class that wraps a method with an instance that can be invoked. + */ + public static class MethodOnInstance { + + private final Object instance; + private final Method method; + + public MethodOnInstance(Object instance, Method field) { + this.instance = instance; + this.method = field; + } + + public Method getMethod() { + return method; + } + + public Object getInstance() { + return instance; + } + + public Object invoke() throws IllegalAccessException { + return ReflectUtils.invokeMethod(method, instance); + } + } +} diff --git a/src/main/java/fr/irstea/associatione/model/gui/GUI.java b/src/main/java/fr/irstea/associatione/model/gui/GUI.java index 7103ad99fe8c42d4910de2b5d511744340a66886..2245d0ddfba112cf930a83df0474efdbdfe214cd 100644 --- a/src/main/java/fr/irstea/associatione/model/gui/GUI.java +++ b/src/main/java/fr/irstea/associatione/model/gui/GUI.java @@ -20,21 +20,7 @@ import fr.irstea.associatione.model.Model; import fr.irstea.associatione.model.Parameters; import fr.irstea.associatione.model.ProcessingException; import fr.irstea.associatione.model.distorsion.DistorsionFromOpinions; -import java.awt.GridLayout; -import java.util.Arrays; import javax.swing.JFrame; -import javax.swing.JPanel; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.StandardXYBarPainter; -import org.jfree.chart.renderer.xy.XYBarRenderer; -import org.jfree.data.statistics.HistogramDataset; -import org.jfree.data.statistics.HistogramType; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; /** * @@ -43,27 +29,20 @@ import org.jfree.data.xy.XYSeriesCollection; public class GUI { private final JFrame frame; - private final JPanel chartsPanel; + private final TimeSerieChart timeSerieChart; private Model model; - private ChartPanel credibilityChartPanel; - private final XYSeriesCollection opinionsMeanOnSubjects; public GUI() { frame = new JFrame("test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.chartsPanel = new JPanel(new GridLayout(0, 2)); - frame.setContentPane(chartsPanel); - opinionsMeanOnSubjects = new XYSeriesCollection(); + timeSerieChart = new TimeSerieChart(); + frame.setContentPane(timeSerieChart.getDisplay()); } - public void initCharts() { - for (int i = 0; i < model.getParameters().getPopSize(); i++) { - opinionsMeanOnSubjects.addSeries(new XYSeries(i)); - } - chartsPanel.removeAll(); - credibilityChartPanel = new ChartPanel(createHistogram("Credibility", model.getCredibility())); - chartsPanel.add(credibilityChartPanel); - chartsPanel.add(new ChartPanel(ChartFactory.createXYLineChart("Opinions means on subjects", "timesteps", "opinions", opinionsMeanOnSubjects, PlotOrientation.VERTICAL, false, true, false))); + public void initCharts() throws ProcessingException { + timeSerieChart.addTimeSerie(model, "getOpinionsMeanOnSubjects", "Opinions", true, true); + timeSerieChart.addTimeSerie(model, "getSubjectiveNorm", "Subjective norm", true, true); + timeSerieChart.addTimeSerie(model, "getCredibility", "Credibility", true, false); } public void init(Parameters parameters) throws ProcessingException, IllegalArgumentException, IllegalAccessException { @@ -72,53 +51,15 @@ public class GUI { initCharts(); frame.pack(); frame.setVisible(true); - updatePlots(model.getTimestep()); + timeSerieChart.updateCharts(); for (int i = 0; i < 50; i++) { model.iter(); - updatePlots(model.getTimestep()); + timeSerieChart.updateCharts(); } } - public void test() { - double[] data = new double[]{0.3, 0.3, 0.5, 0.6, 0.65}; - JFrame f = new JFrame("test"); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.setContentPane(new ChartPanel(createHistogram("Credibility", data))); - f.pack(); - f.setVisible(true); - } - - public void updatePlots(int timestep) { - credibilityChartPanel.setChart(createHistogram("Credibility", model.getCredibility())); - double[] data = model.getOpinionsMeanOnSubjects(); - for (int i = 0; i < data.length; i++) { - opinionsMeanOnSubjects.getSeries(i).add(timestep, data[i]); - } - } - - public static JFreeChart createHistogram(String title, double[] data) { - HistogramDataset dataset = new HistogramDataset(); - dataset.setType(HistogramType.SCALE_AREA_TO_1); - dataset.addSeries(title, data, 30); - String plotTitle = title; - String xaxis = "opinions"; - String yaxis = "amount"; - PlotOrientation orientation = PlotOrientation.VERTICAL; - boolean show = false; - boolean toolTips = false; - boolean urls = false; - JFreeChart chart = ChartFactory.createHistogram(plotTitle, xaxis, yaxis, - dataset, orientation, show, toolTips, urls); - XYPlot plot = (XYPlot) chart.getPlot(); - XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer(); - renderer.setBarPainter(new StandardXYBarPainter()); - renderer.setShadowVisible(false); - return chart; - } - public static void main(String[] args) throws Exception { GUI gui = new GUI(); gui.init(new Parameters(0, 50, 1, new DistorsionFromOpinions(), 1, 0.3)); - } } diff --git a/src/main/java/fr/irstea/associatione/model/gui/TimeSerieChart.java b/src/main/java/fr/irstea/associatione/model/gui/TimeSerieChart.java new file mode 100644 index 0000000000000000000000000000000000000000..b353aed5aa45939ec7285647e85837a33fb678c9 --- /dev/null +++ b/src/main/java/fr/irstea/associatione/model/gui/TimeSerieChart.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2016 Irstea + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package fr.irstea.associatione.model.gui; + +import fr.irstea.associatione.model.Model; +import fr.irstea.associatione.model.ProcessingException; +import fr.irstea.associatione.model.ReflectUtils; +import java.awt.GridLayout; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JPanel; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.StandardXYBarPainter; +import org.jfree.chart.renderer.xy.XYBarRenderer; +import org.jfree.data.statistics.HistogramDataset; +import org.jfree.data.statistics.HistogramType; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * + * @author Nicolas Dumoulin <nicolas.dumoulin@irstea.fr> + */ +public class TimeSerieChart { + + private JPanel display; + private List<Timeserie> timeseries; + + public static class Timeserie { + + private final String name; + private final boolean histogram, series; + private final ReflectUtils.MethodOnInstance methodOnInstance; + private final List<double[]> timeserie; + private final ChartPanel histogramPanel; + private final XYSeriesCollection xySeries; + + public Timeserie(Model model, String fetcherPath, String name, boolean histogram, boolean series) throws ProcessingException { + this.name = name; + this.histogram = histogram; + this.series = series; + try { + this.methodOnInstance = ReflectUtils.getMethodOnInstance(model, fetcherPath); + this.timeserie = new ArrayList<>(); + xySeries = new XYSeriesCollection(); + histogramPanel = new ChartPanel(createHistogram(name, (double[]) methodOnInstance.invoke())); + } catch (NoSuchFieldException | IllegalAccessException ex) { + throw new ProcessingException("Error during method retrieval", ex); + } + } + + private JPanel getHistogram() { + return histogramPanel; + } + + private JPanel getSeries() { + return new ChartPanel(ChartFactory.createXYLineChart(name, "timesteps", name, xySeries, PlotOrientation.VERTICAL, false, true, false)); + } + + public void add(double[] data) { + timeserie.add(data); + } + + public void update() throws IllegalAccessException { + final double[] data = (double[]) methodOnInstance.invoke(); + histogramPanel.setChart(createHistogram(name, data)); + timeserie.add(data); + if (xySeries.getSeriesCount() == 0) { + for (int i = 0; i < data.length; i++) { + xySeries.addSeries(new XYSeries(i)); + } + } + for (int i = 0; i < data.length; i++) { + xySeries.getSeries(i).add(timeserie.size() - 1, data[i]); + } + } + + } + + public TimeSerieChart() { + display = new JPanel(new GridLayout(0, 2)); + timeseries = new ArrayList<>(); + } + + public JPanel getDisplay() { + return display; + } + + public void addTimeSerie(Model model, String fetcherPath, String name, boolean histogram, boolean series) throws ProcessingException { + final Timeserie timeserie = new Timeserie(model, fetcherPath, name, histogram, series); + timeseries.add(timeserie); + if (histogram) { + display.add(timeserie.getHistogram()); + } + if (series) { + display.add(timeserie.getSeries()); + } + } + + public void updateCharts() { + for (Timeserie timeserie : timeseries) { + try { + timeserie.update(); + } catch (IllegalAccessException ex) { + Logger.getLogger(TimeSerieChart.class.getName()).log(Level.SEVERE, null, ex); + } + + } + } + + public static JFreeChart createHistogram(String title, double[] data) { + HistogramDataset dataset = new HistogramDataset(); + dataset.setType(HistogramType.SCALE_AREA_TO_1); + dataset.addSeries(title, data, 30); + String plotTitle = title; + String xaxis = "opinions"; + String yaxis = "amount"; + PlotOrientation orientation = PlotOrientation.VERTICAL; + boolean show = false; + boolean toolTips = false; + boolean urls = false; + JFreeChart chart = ChartFactory.createHistogram(plotTitle, xaxis, yaxis, + dataset, orientation, show, toolTips, urls); + XYPlot plot = (XYPlot) chart.getPlot(); + XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer(); + renderer.setBarPainter(new StandardXYBarPainter()); + renderer.setShadowVisible(false); + return chart; + } + +}