001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.corrector;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.GridBagLayout;
007    import java.util.ArrayList;
008    import java.util.Collection;
009    import java.util.Collections;
010    import java.util.HashMap;
011    import java.util.HashSet;
012    import java.util.List;
013    import java.util.Map;
014    import java.util.Set;
015    import java.util.Map.Entry;
016    
017    import javax.swing.JLabel;
018    import javax.swing.JOptionPane;
019    import javax.swing.JPanel;
020    import javax.swing.JScrollPane;
021    
022    import org.openstreetmap.josm.Main;
023    import org.openstreetmap.josm.command.ChangeCommand;
024    import org.openstreetmap.josm.command.ChangeRelationMemberRoleCommand;
025    import org.openstreetmap.josm.command.Command;
026    import org.openstreetmap.josm.data.osm.Node;
027    import org.openstreetmap.josm.data.osm.OsmPrimitive;
028    import org.openstreetmap.josm.data.osm.Relation;
029    import org.openstreetmap.josm.data.osm.Way;
030    import org.openstreetmap.josm.gui.DefaultNameFormatter;
031    import org.openstreetmap.josm.gui.JMultilineLabel;
032    import org.openstreetmap.josm.tools.GBC;
033    import 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    
043    public 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 property 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("Properties 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    }