// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.corrector;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.command.ChangeCommand;
import org.openstreetmap.josm.command.ChangeRelationMemberRoleCommand;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.DefaultNameFormatter;
import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
import org.openstreetmap.josm.tools.GBC;
import org.openstreetmap.josm.tools.ImageProvider;
/**
* Abstract base class for automatic tag corrections.
*
* Subclasses call applyCorrections() with maps of the requested
* corrections and a dialog is pesented to the user to
* confirm these changes.
* @param
The type of OSM primitive to correct
*/
public abstract class TagCorrector
{
public abstract Collection execute(P oldprimitive, P primitive) throws UserCancelException;
private String[] applicationOptions = new String[] {
tr("Apply selected changes"),
tr("Do not apply changes"),
tr("Cancel")
};
protected Collection applyCorrections(
Map> tagCorrectionsMap,
Map> roleCorrectionMap,
String description) throws UserCancelException {
if (!tagCorrectionsMap.isEmpty() || !roleCorrectionMap.isEmpty()) {
Collection commands = new ArrayList<>();
Map tagTableMap = new HashMap<>();
Map roleTableMap = new HashMap<>();
final JPanel p = new JPanel(new GridBagLayout());
final JMultilineLabel label1 = new JMultilineLabel(description);
label1.setMaxWidth(600);
p.add(label1, GBC.eop().anchor(GBC.CENTER).fill(GBC.HORIZONTAL));
final JMultilineLabel label2 = new JMultilineLabel(
tr("Please select which changes you want to apply."));
label2.setMaxWidth(600);
p.add(label2, GBC.eop().anchor(GBC.CENTER).fill(GBC.HORIZONTAL));
for (Entry> entry : tagCorrectionsMap.entrySet()) {
final OsmPrimitive primitive = entry.getKey();
final List tagCorrections = entry.getValue();
if (tagCorrections.isEmpty()) {
continue;
}
final JLabel propertiesLabel = new JLabel(tr("Tags of "));
p.add(propertiesLabel, GBC.std());
final JLabel primitiveLabel = new JLabel(
primitive.getDisplayName(DefaultNameFormatter.getInstance()) + ":",
ImageProvider.get(primitive.getDisplayType()),
JLabel.LEFT
);
p.add(primitiveLabel, GBC.eol());
final TagCorrectionTable table = new TagCorrectionTable(
tagCorrections);
final JScrollPane scrollPane = new JScrollPane(table);
p.add(scrollPane, GBC.eop().fill(GBC.HORIZONTAL));
tagTableMap.put(primitive, table);
}
for (Entry> entry : roleCorrectionMap.entrySet()) {
final OsmPrimitive primitive = entry.getKey();
final List roleCorrections = entry.getValue();
if (roleCorrections.isEmpty()) {
continue;
}
final JLabel rolesLabel = new JLabel(tr("Roles in relations referring to"));
p.add(rolesLabel, GBC.std());
final JLabel primitiveLabel = new JLabel(
primitive.getDisplayName(DefaultNameFormatter.getInstance()),
ImageProvider.get(primitive.getDisplayType()),
JLabel.LEFT
);
p.add(primitiveLabel, GBC.eol());
final RoleCorrectionTable table = new RoleCorrectionTable(roleCorrections);
final JScrollPane scrollPane = new JScrollPane(table);
p.add(scrollPane, GBC.eop().fill(GBC.HORIZONTAL));
roleTableMap.put(primitive, table);
}
int answer = JOptionPane.showOptionDialog(
Main.parent,
p,
tr("Automatic tag correction"),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE,
null,
applicationOptions,
applicationOptions[0]
);
if (answer == JOptionPane.YES_OPTION) {
for (Entry> entry : tagCorrectionsMap.entrySet()) {
List tagCorrections = entry.getValue();
OsmPrimitive primitive = entry.getKey();
// create the clone
OsmPrimitive clone = null;
if (primitive instanceof Way) {
clone = new Way((Way)primitive);
} else if (primitive instanceof Node) {
clone = new Node((Node)primitive);
} else if (primitive instanceof Relation) {
clone = new Relation((Relation)primitive);
} else
throw new AssertionError();
// use this structure to remember keys that have been set already so that
// they're not dropped by a later step
Set keysChanged = new HashSet<>();
// apply all changes to this clone
for (int i = 0; i < tagCorrections.size(); i++) {
if (tagTableMap.get(primitive).getCorrectionTableModel().getApply(i)) {
TagCorrection tagCorrection = tagCorrections.get(i);
if (tagCorrection.isKeyChanged() && !keysChanged.contains(tagCorrection.oldKey)) {
clone.remove(tagCorrection.oldKey);
}
clone.put(tagCorrection.newKey, tagCorrection.newValue);
keysChanged.add(tagCorrection.newKey);
}
}
// save the clone
if (!keysChanged.isEmpty()) {
commands.add(new ChangeCommand(primitive, clone));
}
}
for (Entry> entry : roleCorrectionMap.entrySet()) {
OsmPrimitive primitive = entry.getKey();
List roleCorrections = entry.getValue();
for (int i = 0; i < roleCorrections.size(); i++) {
RoleCorrection roleCorrection = roleCorrections.get(i);
if (roleTableMap.get(primitive).getCorrectionTableModel().getApply(i)) {
commands.add(new ChangeRelationMemberRoleCommand(roleCorrection.relation, roleCorrection.position, roleCorrection.newRole));
}
}
}
} else if (answer != JOptionPane.NO_OPTION)
throw new UserCancelException();
return commands;
}
return Collections.emptyList();
}
}