001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.advanced;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.ArrayList;
007import java.util.Arrays;
008import java.util.Collections;
009import java.util.LinkedHashMap;
010import java.util.List;
011import java.util.Map;
012import java.util.Map.Entry;
013
014import javax.swing.JComponent;
015import javax.swing.JPanel;
016import javax.swing.table.AbstractTableModel;
017
018import org.openstreetmap.josm.spi.preferences.MapListSetting;
019
020/**
021 * Editor for List of Maps preference entries.
022 * @since 4634
023 */
024public class MapListEditor extends AbstractTableListEditor<Map<String, String>> {
025
026    private final transient List<List<String>> dataKeys;
027    private final transient List<List<String>> dataValues;
028    private final transient List<String> dataLabels;
029
030    /**
031     * Constructs a new {@code MapListEditor}.
032     * @param gui The parent component
033     * @param entry preference entry
034     * @param setting list of maps setting
035     */
036    public MapListEditor(JComponent gui, PrefEntry entry, MapListSetting setting) {
037        super(gui, tr("Change list of maps setting"), entry);
038        List<Map<String, String>> orig = setting.getValue();
039
040        dataKeys = new ArrayList<>();
041        dataValues = new ArrayList<>();
042        dataLabels = new ArrayList<>();
043        if (orig != null) {
044            int index = 0;
045            for (Map<String, String> m : orig) {
046                List<String> keys = new ArrayList<>();
047                List<String> values = new ArrayList<>();
048                for (Entry<String, String> e : m.entrySet()) {
049                    keys.add(e.getKey());
050                    values.add(e.getValue());
051                }
052                dataKeys.add(keys);
053                dataValues.add(values);
054                dataLabels.add(m.getOrDefault("name", tr("Entry {0}", index + 1)));
055                index++;
056            }
057        }
058    }
059
060    @Override
061    public List<Map<String, String>> getData() {
062        List<Map<String, String>> data = new ArrayList<>();
063        for (int i = 0; i < dataKeys.size(); ++i) {
064            Map<String, String> m = new LinkedHashMap<>();
065            for (int j = 0; j < dataKeys.get(i).size(); ++j) {
066                m.put(dataKeys.get(i).get(j), dataValues.get(i).get(j));
067            }
068            data.add(m);
069        }
070        return data;
071    }
072
073    @Override
074    protected final JPanel build() {
075        table.getTableHeader().getColumnModel().getColumn(0).setHeaderValue(tr("Key"));
076        table.getTableHeader().getColumnModel().getColumn(1).setHeaderValue(tr("Value"));
077        return super.build();
078    }
079
080    private class EntryListModel extends AbstractEntryListModel {
081
082        @Override
083        public String getElementAt(int index) {
084            return dataLabels.get(index);
085        }
086
087        @Override
088        public int getSize() {
089            return dataKeys.size();
090        }
091
092        @Override
093        public void add() {
094            dataKeys.add(new ArrayList<String>());
095            dataValues.add(new ArrayList<String>());
096            dataLabels.add("");
097            fireIntervalAdded(this, getSize() - 1, getSize() - 1);
098        }
099
100        @Override
101        public void remove(int idx) {
102            dataKeys.remove(idx);
103            dataValues.remove(idx);
104            dataLabels.remove(idx);
105            fireIntervalRemoved(this, idx, idx);
106        }
107    }
108
109    private class MapTableModel extends AbstractTableModel {
110
111        private List<List<String>> data() {
112            return entryIdx == null ? Collections.<List<String>>emptyList() : Arrays.asList(dataKeys.get(entryIdx), dataValues.get(entryIdx));
113        }
114
115        private int size() {
116            return entryIdx == null ? 0 : dataKeys.get(entryIdx).size();
117        }
118
119        @Override
120        public int getRowCount() {
121            return entryIdx == null ? 0 : size() + 1;
122        }
123
124        @Override
125        public int getColumnCount() {
126            return 2;
127        }
128
129        @Override
130        public String getColumnName(int column) {
131            return column == 0 ? tr("Key") : tr("Value");
132        }
133
134        @Override
135        public Object getValueAt(int row, int column) {
136            return size() == row ? "" : data().get(column).get(row);
137        }
138
139        @Override
140        public void setValueAt(Object o, int row, int column) {
141            String s = (String) o;
142            if (row == size()) {
143                data().get(0).add("");
144                data().get(1).add("");
145                data().get(column).set(row, s);
146                fireTableRowsInserted(row+1, row+1);
147            } else {
148                data().get(column).set(row, s);
149                fireTableCellUpdated(row, column);
150            }
151        }
152
153        @Override
154        public boolean isCellEditable(int row, int column) {
155            return true;
156        }
157    }
158
159    @Override
160    protected final AbstractEntryListModel newEntryListModel() {
161        return new EntryListModel();
162    }
163
164    @Override
165    protected final AbstractTableModel newTableModel() {
166        return new MapTableModel();
167    }
168}