jdplus.tramoseats.desktop.plugin.anomalydetection.ui.JTsCheckLastList Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdplus-tramoseats-desktop-plugin Show documentation
Show all versions of jdplus-tramoseats-desktop-plugin Show documentation
${project.parent.artifactId} - ${project.artifactId}
The newest version!
/*
* Copyright 2013 National Bank of Belgium
*
* Licensed under the EUPL, Version 1.1 or – as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
package jdplus.tramoseats.desktop.plugin.anomalydetection.ui;
import jdplus.toolkit.base.api.timeseries.TsCollection;
import jdplus.toolkit.desktop.plugin.components.parts.HasTsCollection;
import jdplus.toolkit.desktop.plugin.components.parts.HasTsCollectionSupport;
import jdplus.toolkit.desktop.plugin.DemetraIcons;
import jdplus.tramoseats.desktop.plugin.anomalydetection.AnomalyItem;
import jdplus.toolkit.desktop.plugin.util.ActionMaps;
import jdplus.toolkit.desktop.plugin.util.InputMaps;
import jdplus.toolkit.desktop.plugin.components.JTsTable;
import jdplus.toolkit.desktop.plugin.components.TimeSeriesComponent;
import ec.util.table.swing.JTables;
import ec.util.various.swing.JCommand;
import jdplus.toolkit.desktop.plugin.components.TsSelectionBridge;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.beans.Beans;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableCellRenderer;
import jdplus.toolkit.desktop.plugin.components.TsIdentifier;
import jdplus.toolkit.desktop.plugin.datatransfer.DataTransferManager;
import jdplus.main.desktop.design.SwingComponent;
import jdplus.main.desktop.design.SwingProperty;
import jdplus.toolkit.base.api.timeseries.Ts;
import jdplus.toolkit.base.api.timeseries.TsMoniker;
import jdplus.toolkit.base.api.timeseries.TsPeriod;
import jdplus.tramoseats.base.api.tramo.TramoSpec;
import jdplus.toolkit.base.api.util.MultiLineNameUtil;
import jdplus.toolkit.base.api.util.Table;
import internal.ui.components.DemoTsBuilder;
import java.util.LinkedHashMap;
import jdplus.toolkit.base.core.regsarima.regular.CheckLast;
import jdplus.tramoseats.base.core.tramo.TramoKernel;
import nbbrd.design.SkipProcessing;
/**
* List component containing input and output results of a Check Last batch
* processing
*
* @author Mats Maggi
*/
@SwingComponent
public final class JTsCheckLastList extends JComponent implements TimeSeriesComponent,
HasTsCollection {
@SkipProcessing(target = SwingProperty.class, reason = "to be refactored")
@SwingProperty
public static final String COLOR_VALUES_PROPERTY = "colorValues";
@SwingProperty
public static final String LAST_CHECKS_PROPERTY = "lastChecks";
@SwingProperty
public static final String SPEC_PROPERTY = "spec";
@SkipProcessing(target = SwingProperty.class, reason = "to be refactored")
@SwingProperty
public static final String COLLECTION_CHANGE_PROPERTY = "collectionChange";
@SkipProcessing(target = SwingProperty.class, reason = "to be refactored")
@SwingProperty
public static final String ITEM_SELECTION_PROPERTY = "itemSelection";
@lombok.experimental.Delegate(types = HasTsCollection.class)
private final JTsTable table;
private double orangeCells;
private double redCells;
private int lastChecks;
private TramoSpec spec;
private final LinkedHashMap map = new LinkedHashMap<>();
private CheckLast checkLast;
public JTsCheckLastList() {
this.table = new JTsTable();
this.orangeCells = 4.0;
this.redCells = 5.0;
this.lastChecks = 1;
this.spec = TramoSpec.TRfull;
initTable();
checkLast = new CheckLast(TramoKernel.of(spec, null), 12);
onComponentPopupMenuChange();
enableProperties();
setLayout(new BorderLayout());
add(table, BorderLayout.CENTER);
if (Beans.isDesignTime()) {
setTsCollection(DemoTsBuilder.randomTsCollection(3));
setTsUpdateMode(TsUpdateMode.None);
setPreferredSize(new Dimension(200, 150));
}
}
private void enableProperties() {
this.addPropertyChangeListener(evt -> {
switch (evt.getPropertyName()) {
case TS_COLLECTION_PROPERTY:
onCollectionChange();
break;
case TsSelectionBridge.TS_SELECTION_PROPERTY:
onSelectionChange();
break;
case "componentPopupMenu":
onComponentPopupMenuChange();
break;
case COLOR_VALUES_PROPERTY:
onColorValuesChange();
break;
case LAST_CHECKS_PROPERTY:
onLastChecksChange();
break;
case SPEC_PROPERTY:
onSpecChange();
break;
}
});
}
//
public double getOrangeCells() {
return orangeCells;
}
public void setOrangeCells(double orangeCells) {
if (orangeCells < 0 || orangeCells > redCells) {
throw new IllegalArgumentException("Orange value must be >= 0 and < Red value");
}
double old = this.orangeCells;
this.orangeCells = orangeCells;
firePropertyChange(COLOR_VALUES_PROPERTY, old, this.orangeCells);
}
public double getRedCells() {
return redCells;
}
public void setRedCells(double redCells) {
if (redCells < orangeCells) {
throw new IllegalArgumentException("Red value must be greater than Orange value");
}
double old = this.redCells;
this.redCells = redCells;
firePropertyChange(COLOR_VALUES_PROPERTY, old, this.redCells);
}
public int getLastChecks() {
return lastChecks;
}
public void setLastChecks(int lastChecks) {
if (lastChecks < 1 || lastChecks > 3) {
throw new IllegalArgumentException("Number of last checked values can only be 1, 2 or 3 !");
}
int old = this.lastChecks;
this.lastChecks = lastChecks;
firePropertyChange(LAST_CHECKS_PROPERTY, old, this.lastChecks);
}
public TramoSpec getSpec() {
return spec;
}
public void setSpec(TramoSpec spec) {
TramoSpec old = this.spec;
this.spec = spec;
firePropertyChange(SPEC_PROPERTY, old, this.spec);
}
//
private void resetValues() {
for (AnomalyItem item : map.values()) {
item.reset(lastChecks);
}
checkLast = new CheckLast(TramoKernel.of(spec, null), lastChecks);
}
public CheckLast getCheckLast() {
return checkLast;
}
public Map getMap() {
return map;
}
public AnomalyItem[] getItems() {
AnomalyItem[] all = new AnomalyItem[map.size()];
return map.values().toArray(all);
}
public void fireTableStructureChanged() {
List columns = new ArrayList<>();
columns.add(seriesColumn);
columns.add(lastPeriodColumn);
columns.add(abs1Column);
columns.add(rel1Column);
if (lastChecks > 1) {
columns.add(abs2Column);
columns.add(rel2Column);
}
if (lastChecks > 2) {
columns.add(abs3Column);
columns.add(rel3Column);
}
table.setColumns(columns);
switch (lastChecks) {
case 1:
table.setWidthAsPercentages(new double[]{.7, .1, .1, .1});
break;
case 2:
table.setWidthAsPercentages(new double[]{.5, .1, .1, .1, .1, .1});
break;
case 3:
table.setWidthAsPercentages(new double[]{.3, .1, .1, .1, .1, .1, .1, .1});
break;
}
}
public void fireTableDataChanged() {
table.repaint();
}
private JPopupMenu buildPopupMenu() {
JPopupMenu result = HasTsCollectionSupport.newDefaultMenu(this).getPopupMenu();
int index = 11;
JMenuItem item;
result.insert(new JSeparator(), index++);
JMenu sub = new JMenu("Export results to");
sub.add(new CopyToClipoard().toAction(this)).setText("Clipboard");
result.insert(sub, index++);
item = new JMenuItem(new AbstractAction("Original Order") {
@Override
public void actionPerformed(ActionEvent arg0) {
// table.getRowSorter().setSortKeys(null);
}
});
item.setEnabled(true);
result.insert(item, index++);
return result;
}
private void initTable() {
// table.setMultiSelection(false);
table.getTsSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// result.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
// ((JLabel) result.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(SwingConstants.CENTER);
table.addPropertyChangeListener(evt -> {
switch (evt.getPropertyName()) {
case HasTsCollection.DROP_CONTENT_PROPERTY:
case HasTsCollection.FREEZE_ON_IMPORT_PROPERTY:
case HasTsCollection.TS_COLLECTION_PROPERTY:
case HasTsCollection.TS_SELECTION_MODEL_PROPERTY:
case HasTsCollection.TS_UPDATE_MODE_PROPERTY:
case TsSelectionBridge.TS_SELECTION_PROPERTY:
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
break;
}
});
ActionMaps.copyEntries(getActionMap(), false, table.getActionMap());
InputMaps.copyEntries(getInputMap(), false, table.getInputMap());
fireTableStructureChanged();
}
private void onColorValuesChange() {
fireTableDataChanged();
}
private void onLastChecksChange() {
resetValues();
fireTableStructureChanged();
}
private void onSpecChange() {
resetValues();
fireTableDataChanged();
}
private void onCollectionChange() {
TsCollection collection = getTsCollection();
for (Ts s : collection) {
AnomalyItem item = map.get(s.getMoniker());
if (item == null || item.getData().isEmpty()) {
String name = s.getName();
name = MultiLineNameUtil.join(name);
AnomalyItem a = new AnomalyItem(name, s.getData(), lastChecks);
map.put(s.getMoniker(), a);
}
}
fireTableDataChanged();
firePropertyChange(COLLECTION_CHANGE_PROPERTY, null, collection);
onSelectionChange();
}
private void onSelectionChange() {
Optional fts = table.getTsSelectionStream().findFirst();
AnomalyItem selected = null;
if (fts.isPresent()) {
selected = map.get(fts.orElseThrow().getMoniker());
if (!selected.isProcessed()) {
CheckLast cl = new CheckLast(TramoKernel.of(spec, null), lastChecks);
selected.process(cl);
table.repaint();
}
}
firePropertyChange(ITEM_SELECTION_PROPERTY, null, selected);
}
private Optional getAnomaly(Ts ts) {
AnomalyItem item = map.get(ts.getMoniker());
return Optional.ofNullable(item);
}
private void onComponentPopupMenuChange() {
JPopupMenu popupMenu = getComponentPopupMenu();
table.setComponentPopupMenu(popupMenu != null ? popupMenu : buildPopupMenu());
}
private final JTsTable.Column seriesColumn = JTsTable.Column.builder()
.name("
Series Name
")
.type(Ts.class)
.mapper(ts -> TsIdentifier.of(ts))
.comparator(TS_COMP)
.comparator(JTsTable.Column.TS_IDENTIFIER.getComparator())
.renderer(o -> new Decorator(JTsTable.Column.TS_IDENTIFIER.getRenderer().apply(o)))
.build();
private final JTsTable.Column lastPeriodColumn = JTsTable.Column.builder()
.name(" Last
Period
")
.type(TsPeriod.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.getData().isEmpty() ? null : o.getData().getDomain().getLastPeriod()).orElse(null))
.comparator(JTsTable.Column.LAST.getComparator())
.renderer(o -> new Decorator(JTsTable.Column.LAST.getRenderer().apply(o)))
.build();
private final JTsTable.Column abs1Column = JTsTable.Column.builder()
.name("Abs.
Error
N-1")
.type(Double.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.isProcessed() ? o.getAbsoluteError(0) : null).orElse(null))
.comparator(DOUBLE_COMP)
.renderer(o -> new Decorator(JTables.cellRendererOf(JTsCheckLastList::apply)))
.build();
private final JTsTable.Column rel1Column = JTsTable.Column.builder()
.name("Rel.
Error
N-1")
.type(Double.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.isProcessed() ? o.getRelativeError(0) : null).orElse(null))
.comparator(DOUBLE_COMP)
.renderer(o -> new Decorator(JTables.cellRendererOf(JTsCheckLastList::apply)))
.build();
private final JTsTable.Column abs2Column = JTsTable.Column.builder()
.name("Abs.
Error
N-2")
.type(Double.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.isProcessed() ? o.getAbsoluteError(1) : null).orElse(null))
.comparator(DOUBLE_COMP)
.renderer(o -> new Decorator(JTables.cellRendererOf(JTsCheckLastList::apply)))
.build();
private final JTsTable.Column rel2Column = JTsTable.Column.builder()
.name("Rel.
Error
N-2")
.type(Double.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.isProcessed() ? o.getRelativeError(1) : null).orElse(null))
.comparator(DOUBLE_COMP)
.renderer(o -> new Decorator(JTables.cellRendererOf(JTsCheckLastList::apply)))
.build();
private final JTsTable.Column abs3Column = JTsTable.Column.builder()
.name("Abs.
Error
N-3")
.type(Double.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.isProcessed() ? o.getAbsoluteError(2) : null).orElse(null))
.comparator(DOUBLE_COMP)
.renderer(o -> new Decorator(JTables.cellRendererOf(JTsCheckLastList::apply)))
.build();
private final JTsTable.Column rel3Column = JTsTable.Column.builder()
.name("Rel.
Error
N-3")
.type(Double.class)
.mapper(ts -> getAnomaly(ts).map(o -> o.isProcessed() ? o.getRelativeError(2) : null).orElse(null))
.comparator(DOUBLE_COMP)
.renderer(o -> new Decorator(JTables.cellRendererOf(JTsCheckLastList::apply)))
.build();
private static void apply(JLabel l, Double value) {
l.setHorizontalAlignment(JLabel.TRAILING);
}
public Map getReportParameters() {
Map parameters = new HashMap();
parameters.put("_SPECIFICATION", spec.toString());
parameters.put("_NB_CHECK_LAST", lastChecks);
parameters.put("_NB_OF_SERIES", map.size());
parameters.put("_ORANGE_CELLS", orangeCells);
parameters.put("_RED_CELLS", redCells);
return parameters;
}
private static abstract class ModelCommand extends JCommand {
@Override
public boolean isEnabled(JTsCheckLastList list) {
return !list.getMap().isEmpty();
}
@Override
public JCommand.ActionAdapter toAction(JTsCheckLastList list) {
return super.toAction(list).withWeakPropertyChangeListener(list, COLLECTION_CHANGE_PROPERTY);
}
}
private static Table
© 2015 - 2025 Weber Informatics LLC | Privacy Policy