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