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