it.cnr.istc.iloc.gui.StateVariableVisualizer.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.istc.iloc.gui.StateVariableVisualizer.java

Source

/*
 * Copyright (C) 2017 Riccardo De Benedictis <riccardo.debenedictis@istc.cnr.it>
 *
 * 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 it.cnr.istc.iloc.gui;

import it.cnr.istc.core.Atom;
import it.cnr.istc.core.AtomState;
import static it.cnr.istc.core.Core.BOOL;
import static it.cnr.istc.core.Core.REAL;
import static it.cnr.istc.core.Core.STRING;
import it.cnr.istc.core.Field;
import it.cnr.istc.core.IArithItem;
import it.cnr.istc.core.IBoolItem;
import it.cnr.istc.core.IEnumItem;
import it.cnr.istc.core.IItem;
import static it.cnr.istc.core.IScope.SCOPE;
import it.cnr.istc.core.IStringItem;
import it.cnr.istc.core.Type;
import it.cnr.istc.iloc.SmartType;
import it.cnr.istc.iloc.types.StateVariable;
import it.cnr.istc.utils.gui.ReverseGradientXYBarPainter;
import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.XYItemLabelGenerator;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYIntervalDataItem;
import org.jfree.data.xy.XYIntervalSeries;
import org.jfree.data.xy.XYIntervalSeriesCollection;
import org.jfree.ui.TextAnchor;

/**
 *
 * @author Riccardo De Benedictis <riccardo.debenedictis@istc.cnr.it>
 */
class StateVariableVisualizer implements TimelineVisualizer {

    @Override
    public Class<? extends SmartType> getType() {
        return StateVariable.class;
    }

    @Override
    public Collection<XYPlot> getPlots(Type type) {
        Collection<IItem> instances = type.getInstances();
        Collection<XYPlot> plots = new ArrayList<>(instances.size());
        Map<IItem, Collection<Atom>> sv_atoms = new IdentityHashMap<>(instances.size());
        for (IItem i : type.getInstances()) {
            sv_atoms.put(i, new ArrayList<>());
        }
        for (Atom atom : ((StateVariable) type).getDefinedPredicates().stream()
                .flatMap(p -> p.getInstances().stream()).map(a -> (Atom) a)
                .filter(a -> a.state.evaluate().isSingleton() && a.state.evaluate().contains(AtomState.Active))
                .collect(Collectors.toList())) {
            for (IItem i : ((IEnumItem) atom.get(SCOPE)).getEnumVar().evaluate().getAllowedValues()) {
                if (sv_atoms.containsKey(i)) {
                    sv_atoms.get(i).add(atom);
                }
            }
        }

        for (IItem sv : instances) {
            Collection<Atom> atoms = sv_atoms.get(sv);
            // For each pulse the atoms starting at that pulse
            Map<Double, Collection<Atom>> starting_atoms = new HashMap<>(atoms.size());
            // For each pulse the atoms ending at that pulse
            Map<Double, Collection<Atom>> ending_atoms = new HashMap<>(atoms.size());

            // The pulses of the timeline
            Set<Double> c_pulses = new HashSet<>(atoms.size() * 2);

            for (Atom atom : atoms) {
                double start = sv.getCore().evaluate(((IArithItem) atom.get("start")).getArithVar());
                double end = sv.getCore().evaluate(((IArithItem) atom.get("end")).getArithVar());

                if (!starting_atoms.containsKey(start)) {
                    starting_atoms.put(start, new ArrayList<>());
                }
                starting_atoms.get(start).add(atom);

                if (!ending_atoms.containsKey(end)) {
                    ending_atoms.put(end, new ArrayList<>());
                }
                ending_atoms.get(end).add(atom);

                c_pulses.add(start);
                c_pulses.add(end);
            }

            // we sort current pulses..
            Double[] c_pulses_array = c_pulses.toArray(new Double[c_pulses.size()]);
            Arrays.sort(c_pulses_array);

            XYIntervalSeriesCollection collection = new XYIntervalSeriesCollection();

            ValueXYIntervalSeries undefined = new ValueXYIntervalSeries("Undefined");
            ValueXYIntervalSeries sv_values = new ValueXYIntervalSeries("Values");
            ValueXYIntervalSeries conflicts = new ValueXYIntervalSeries("Conflicts");

            List<Atom> overlapping_atoms = new ArrayList<>();
            for (int i = 0; i < c_pulses_array.length - 1; i++) {
                if (starting_atoms.containsKey(c_pulses_array[i])) {
                    overlapping_atoms.addAll(starting_atoms.get(c_pulses_array[i]));
                }
                if (ending_atoms.containsKey(c_pulses_array[i])) {
                    overlapping_atoms.removeAll(ending_atoms.get(c_pulses_array[i]));
                }
                switch (overlapping_atoms.size()) {
                case 0:
                    undefined.add(c_pulses_array[i], c_pulses_array[i], c_pulses_array[i + 1], 0, 0, 1,
                            new Atom[0]);
                    break;
                case 1:
                    sv_values.add(c_pulses_array[i], c_pulses_array[i], c_pulses_array[i + 1], 0, 0, 1,
                            overlapping_atoms.toArray(new Atom[overlapping_atoms.size()]));
                    break;
                default:
                    conflicts.add(c_pulses_array[i], c_pulses_array[i], c_pulses_array[i + 1], 0, 0, 1,
                            overlapping_atoms.toArray(new Atom[overlapping_atoms.size()]));
                    break;
                }
            }

            collection.addSeries(undefined);
            collection.addSeries(sv_values);
            collection.addSeries(conflicts);

            XYBarRenderer renderer = new XYBarRenderer();
            renderer.setSeriesPaint(0, Color.lightGray);
            renderer.setSeriesPaint(1, new Color(100, 250, 100));
            renderer.setSeriesPaint(2, Color.pink);
            renderer.setBarPainter(new ReverseGradientXYBarPainter());
            renderer.setDrawBarOutline(true);
            renderer.setShadowXOffset(2);
            renderer.setShadowYOffset(2);
            renderer.setUseYInterval(true);

            renderer.setBaseItemLabelsVisible(true);
            renderer.setBaseItemLabelPaint(Color.black);
            Font font = new Font("SansSerif", Font.PLAIN, 9);
            renderer.setBaseItemLabelFont(font);
            XYItemLabelGenerator generator = (XYDataset dataset, int series,
                    int item) -> toString(((ValueXYIntervalDataItem) ((XYIntervalSeriesCollection) dataset)
                            .getSeries(series).getDataItem(item)).atoms);
            ItemLabelPosition itLabPos = new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER);
            renderer.setBasePositiveItemLabelPosition(itLabPos);
            for (int i = 0; i < collection.getSeriesCount(); i++) {
                renderer.setSeriesItemLabelGenerator(i, generator);
                renderer.setSeriesItemLabelsVisible(i, true);
                renderer.setSeriesItemLabelPaint(i, Color.black);
                renderer.setSeriesItemLabelFont(i, font);
                renderer.setSeriesPositiveItemLabelPosition(i, itLabPos);
                renderer.setSeriesToolTipGenerator(i,
                        (XYDataset dataset, int series, int item) -> toString(
                                ((ValueXYIntervalDataItem) ((XYIntervalSeriesCollection) dataset).getSeries(series)
                                        .getDataItem(item)).atoms));
            }

            XYPlot plot = new XYPlot(collection, null, new NumberAxis(""), renderer);
            plot.getRangeAxis().setVisible(false);
            plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);

            plots.add(plot);
        }

        return plots;
    }

    @Override
    public void mouseClicked(Object dataItem) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    private static String toString(Atom atom) {
        StringBuilder sb = new StringBuilder();
        sb.append(atom.type.name).append("(");
        LinkedList<Type> queue = new LinkedList<>();
        queue.add(atom.type);
        while (!queue.isEmpty()) {
            Type c_type = queue.pollFirst();
            queue.addAll(c_type.getSuperclasses());
            for (Field field : c_type.getFields()) {
                if (!field.synthetic && !field.name.equals(SCOPE)) {
                    IItem item = atom.get(field.name);
                    sb.append(", ").append(field.name);
                    switch (field.type.name) {
                    case BOOL:
                        sb.append(" = ").append(((IBoolItem) item).getBoolVar().evaluate());
                        break;
                    case REAL:
                        sb.append(" = ").append(atom.core.evaluate(((IArithItem) item).getArithVar()));
                        break;
                    case STRING:
                        sb.append(" = ").append(((IStringItem) item).getValue());
                        break;
                    }
                }
            }
        }
        sb.append(")");
        return sb.toString().replace("(, ", "(");
    }

    private static String toString(Atom[] atoms) {
        switch (atoms.length) {
        case 0:
            return "";
        case 1:
            return toString(atoms[0]);
        default:
            return Stream.of(atoms).map(tk -> toString(tk)).collect(Collectors.joining(", "));
        }
    }

    private static class ValueXYIntervalSeries extends XYIntervalSeries {

        private ValueXYIntervalSeries(Comparable<?> key) {
            super(key);
        }

        public void add(double x, double xLow, double xHigh, double y, double yLow, double yHigh, Atom[] atoms) {
            super.add(new ValueXYIntervalDataItem(x, xLow, xHigh, y, yLow, yHigh, atoms), true);
        }

        @Override
        public Object clone() throws CloneNotSupportedException {
            return super.clone(); //To change body of generated methods, choose Tools | Templates.
        }
    }

    private static class ValueXYIntervalDataItem extends XYIntervalDataItem {

        private final Atom[] atoms;

        private ValueXYIntervalDataItem(double x, double xLow, double xHigh, double y, double yLow, double yHigh,
                Atom[] atoms) {
            super(x, xLow, xHigh, y, yLow, yHigh);
            this.atoms = atoms;
        }

        @Override
        public Object clone() throws CloneNotSupportedException {
            return super.clone(); //To change body of generated methods, choose Tools | Templates.
        }
    }
}