001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.corrector; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.GridBagLayout; 007import java.util.ArrayList; 008import java.util.Collection; 009import java.util.Collections; 010import java.util.HashMap; 011import java.util.HashSet; 012import java.util.List; 013import java.util.Map; 014import java.util.Set; 015import java.util.Map.Entry; 016 017import javax.swing.JLabel; 018import javax.swing.JOptionPane; 019import javax.swing.JPanel; 020import javax.swing.JScrollPane; 021 022import org.openstreetmap.josm.Main; 023import org.openstreetmap.josm.command.ChangeCommand; 024import org.openstreetmap.josm.command.ChangeRelationMemberRoleCommand; 025import org.openstreetmap.josm.command.Command; 026import org.openstreetmap.josm.data.osm.Node; 027import org.openstreetmap.josm.data.osm.OsmPrimitive; 028import org.openstreetmap.josm.data.osm.Relation; 029import org.openstreetmap.josm.data.osm.Way; 030import org.openstreetmap.josm.gui.DefaultNameFormatter; 031import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 032import org.openstreetmap.josm.tools.GBC; 033import org.openstreetmap.josm.tools.ImageProvider; 034 035/** 036 * Abstract base class for automatic tag corrections. 037 * 038 * Subclasses call applyCorrections() with maps of the requested 039 * corrections and a dialog is pesented to the user to 040 * confirm these changes. 041 * @param <P> The type of OSM primitive to correct 042 */ 043public abstract class TagCorrector<P extends OsmPrimitive> { 044 045 public abstract Collection<Command> execute(P oldprimitive, P primitive) throws UserCancelException; 046 047 private String[] applicationOptions = new String[] { 048 tr("Apply selected changes"), 049 tr("Do not apply changes"), 050 tr("Cancel") 051 }; 052 053 protected Collection<Command> applyCorrections( 054 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap, 055 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap, 056 String description) throws UserCancelException { 057 058 if (!tagCorrectionsMap.isEmpty() || !roleCorrectionMap.isEmpty()) { 059 Collection<Command> commands = new ArrayList<>(); 060 Map<OsmPrimitive, TagCorrectionTable> tagTableMap = new HashMap<>(); 061 Map<OsmPrimitive, RoleCorrectionTable> roleTableMap = new HashMap<>(); 062 063 final JPanel p = new JPanel(new GridBagLayout()); 064 065 final JMultilineLabel label1 = new JMultilineLabel(description); 066 label1.setMaxWidth(600); 067 p.add(label1, GBC.eop().anchor(GBC.CENTER).fill(GBC.HORIZONTAL)); 068 069 final JMultilineLabel label2 = new JMultilineLabel( 070 tr("Please select which changes you want to apply.")); 071 label2.setMaxWidth(600); 072 p.add(label2, GBC.eop().anchor(GBC.CENTER).fill(GBC.HORIZONTAL)); 073 074 for (Entry<OsmPrimitive, List<TagCorrection>> entry : tagCorrectionsMap.entrySet()) { 075 final OsmPrimitive primitive = entry.getKey(); 076 final List<TagCorrection> tagCorrections = entry.getValue(); 077 078 if (tagCorrections.isEmpty()) { 079 continue; 080 } 081 082 final JLabel propertiesLabel = new JLabel(tr("Tags of ")); 083 p.add(propertiesLabel, GBC.std()); 084 085 final JLabel primitiveLabel = new JLabel( 086 primitive.getDisplayName(DefaultNameFormatter.getInstance()) + ":", 087 ImageProvider.get(primitive.getDisplayType()), 088 JLabel.LEFT 089 ); 090 p.add(primitiveLabel, GBC.eol()); 091 092 final TagCorrectionTable table = new TagCorrectionTable( 093 tagCorrections); 094 final JScrollPane scrollPane = new JScrollPane(table); 095 p.add(scrollPane, GBC.eop().fill(GBC.HORIZONTAL)); 096 097 tagTableMap.put(primitive, table); 098 } 099 100 for (Entry<OsmPrimitive, List<RoleCorrection>> entry : roleCorrectionMap.entrySet()) { 101 final OsmPrimitive primitive = entry.getKey(); 102 final List<RoleCorrection> roleCorrections = entry.getValue(); 103 104 if (roleCorrections.isEmpty()) { 105 continue; 106 } 107 108 final JLabel rolesLabel = new JLabel(tr("Roles in relations referring to")); 109 p.add(rolesLabel, GBC.std()); 110 111 final JLabel primitiveLabel = new JLabel( 112 primitive.getDisplayName(DefaultNameFormatter.getInstance()), 113 ImageProvider.get(primitive.getDisplayType()), 114 JLabel.LEFT 115 ); 116 p.add(primitiveLabel, GBC.eol()); 117 118 final RoleCorrectionTable table = new RoleCorrectionTable(roleCorrections); 119 final JScrollPane scrollPane = new JScrollPane(table); 120 p.add(scrollPane, GBC.eop().fill(GBC.HORIZONTAL)); 121 122 roleTableMap.put(primitive, table); 123 } 124 125 int answer = JOptionPane.showOptionDialog( 126 Main.parent, 127 p, 128 tr("Automatic tag correction"), 129 JOptionPane.YES_NO_CANCEL_OPTION, 130 JOptionPane.PLAIN_MESSAGE, 131 null, 132 applicationOptions, 133 applicationOptions[0] 134 ); 135 136 if (answer == JOptionPane.YES_OPTION) { 137 for (Entry<OsmPrimitive, List<TagCorrection>> entry : tagCorrectionsMap.entrySet()) { 138 List<TagCorrection> tagCorrections = entry.getValue(); 139 OsmPrimitive primitive = entry.getKey(); 140 141 // create the clone 142 OsmPrimitive clone = null; 143 if (primitive instanceof Way) { 144 clone = new Way((Way)primitive); 145 } else if (primitive instanceof Node) { 146 clone = new Node((Node)primitive); 147 } else if (primitive instanceof Relation) { 148 clone = new Relation((Relation)primitive); 149 } else 150 throw new AssertionError(); 151 152 // use this structure to remember keys that have been set already so that 153 // they're not dropped by a later step 154 Set<String> keysChanged = new HashSet<>(); 155 156 // apply all changes to this clone 157 for (int i = 0; i < tagCorrections.size(); i++) { 158 if (tagTableMap.get(primitive).getCorrectionTableModel().getApply(i)) { 159 TagCorrection tagCorrection = tagCorrections.get(i); 160 if (tagCorrection.isKeyChanged() && !keysChanged.contains(tagCorrection.oldKey)) { 161 clone.remove(tagCorrection.oldKey); 162 } 163 clone.put(tagCorrection.newKey, tagCorrection.newValue); 164 keysChanged.add(tagCorrection.newKey); 165 } 166 } 167 168 // save the clone 169 if (!keysChanged.isEmpty()) { 170 commands.add(new ChangeCommand(primitive, clone)); 171 } 172 } 173 for (Entry<OsmPrimitive, List<RoleCorrection>> entry : roleCorrectionMap.entrySet()) { 174 OsmPrimitive primitive = entry.getKey(); 175 List<RoleCorrection> roleCorrections = entry.getValue(); 176 177 for (int i = 0; i < roleCorrections.size(); i++) { 178 RoleCorrection roleCorrection = roleCorrections.get(i); 179 if (roleTableMap.get(primitive).getCorrectionTableModel().getApply(i)) { 180 commands.add(new ChangeRelationMemberRoleCommand(roleCorrection.relation, roleCorrection.position, roleCorrection.newRole)); 181 } 182 } 183 } 184 } else if (answer != JOptionPane.NO_OPTION) 185 throw new UserCancelException(); 186 return commands; 187 } 188 189 return Collections.emptyList(); 190 } 191}