001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.command;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trn;
006
007import java.util.ArrayList;
008import java.util.Collection;
009import java.util.Collections;
010import java.util.LinkedList;
011import java.util.List;
012import java.util.Objects;
013
014import javax.swing.Icon;
015
016import org.openstreetmap.josm.data.osm.OsmPrimitive;
017import org.openstreetmap.josm.data.validation.util.NameVisitor;
018import org.openstreetmap.josm.tools.ImageProvider;
019
020/**
021 * Command that replaces the key of one or several objects
022 * @since 3669
023 */
024public class ChangePropertyKeyCommand extends Command {
025    static final class SinglePrimitivePseudoCommand implements PseudoCommand {
026        private final String name;
027        private final OsmPrimitive osm;
028        private final Icon icon;
029
030        SinglePrimitivePseudoCommand(String name, OsmPrimitive osm, Icon icon) {
031            this.name = name;
032            this.osm = osm;
033            this.icon = icon;
034        }
035
036        @Override
037        public String getDescriptionText() {
038            return name;
039        }
040
041        @Override
042        public Icon getDescriptionIcon() {
043            return icon;
044        }
045
046        @Override
047        public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
048            return Collections.singleton(osm);
049        }
050    }
051
052    /**
053     * All primitives, that are affected with this command.
054     */
055    private final List<? extends OsmPrimitive> objects;
056    /**
057     * The key that is subject to change.
058     */
059    private final String key;
060    /**
061     * The mew key.
062     */
063    private final String newKey;
064
065    /**
066     * Constructs a new {@code ChangePropertyKeyCommand}.
067     *
068     * @param object the object subject to change replacement
069     * @param key The key to replace
070     * @param newKey the new value of the key
071     * @since 6329
072     */
073    public ChangePropertyKeyCommand(OsmPrimitive object, String key, String newKey) {
074        this(Collections.singleton(object), key, newKey);
075    }
076
077    /**
078     * Constructs a new {@code ChangePropertyKeyCommand}.
079     *
080     * @param objects all objects subject to change replacement
081     * @param key The key to replace
082     * @param newKey the new value of the key
083     */
084    public ChangePropertyKeyCommand(Collection<? extends OsmPrimitive> objects, String key, String newKey) {
085        this.objects = new LinkedList<>(objects);
086        this.key = key;
087        this.newKey = newKey;
088    }
089
090    @Override
091    public boolean executeCommand() {
092        if (!super.executeCommand())
093            return false; // save old
094        for (OsmPrimitive osm : objects) {
095            if (osm.hasKey(key) || osm.hasKey(newKey)) {
096                osm.setModified(true);
097                String oldValue = osm.get(key);
098                osm.put(newKey, oldValue);
099                osm.remove(key);
100            }
101        }
102        return true;
103    }
104
105    @Override
106    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
107        modified.addAll(objects);
108    }
109
110    @Override
111    public String getDescriptionText() {
112        String text = tr("Replace \"{0}\" by \"{1}\" for", key, newKey);
113        if (objects.size() == 1) {
114            NameVisitor v = new NameVisitor();
115            objects.get(0).accept(v);
116            text += " "+tr(v.className)+" "+v.name;
117        } else {
118            text += " "+objects.size()+" "+trn("object", "objects", objects.size());
119        }
120        return text;
121    }
122
123    @Override
124    public Icon getDescriptionIcon() {
125        return ImageProvider.get("data", "key");
126    }
127
128    @Override
129    public Collection<PseudoCommand> getChildren() {
130        if (objects.size() == 1)
131            return null;
132        List<PseudoCommand> children = new ArrayList<>();
133
134        final NameVisitor v = new NameVisitor();
135        for (final OsmPrimitive osm : objects) {
136            osm.accept(v);
137            children.add(new SinglePrimitivePseudoCommand(v.name, osm, v.icon));
138        }
139        return children;
140    }
141
142    @Override
143    public int hashCode() {
144        return Objects.hash(super.hashCode(), objects, key, newKey);
145    }
146
147    @Override
148    public boolean equals(Object obj) {
149        if (this == obj) return true;
150        if (obj == null || getClass() != obj.getClass()) return false;
151        if (!super.equals(obj)) return false;
152        ChangePropertyKeyCommand that = (ChangePropertyKeyCommand) obj;
153        return Objects.equals(objects, that.objects) &&
154                Objects.equals(key, that.key) &&
155                Objects.equals(newKey, that.newKey);
156    }
157}