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