001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.command;
003
004import static org.openstreetmap.josm.tools.I18n.marktr;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.util.Collection;
008import java.util.Collections;
009import java.util.Objects;
010
011import javax.swing.Icon;
012
013import org.openstreetmap.josm.data.osm.OsmPrimitive;
014import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
015import org.openstreetmap.josm.data.osm.Way;
016import org.openstreetmap.josm.gui.DefaultNameFormatter;
017import org.openstreetmap.josm.gui.layer.OsmDataLayer;
018import org.openstreetmap.josm.tools.ImageProvider;
019
020/**
021 * A command that adds an osm primitive to a dataset. Keys cannot be added this way.
022 *
023 * See {@link ChangeCommand} for comments on relation back references.
024 *
025 * @author imi
026 */
027public class AddCommand extends Command {
028
029    /**
030     * The primitive to add to the dataset.
031     */
032    private final OsmPrimitive osm;
033
034    /**
035     * Creates the command and specify the element to add in the context of the current edit layer, if any.
036     * @param osm The primitive to add
037     */
038    public AddCommand(OsmPrimitive osm) {
039        this.osm = Objects.requireNonNull(osm, "osm");
040    }
041
042    /**
043     * Creates the command and specify the element to add in the context of the given data layer.
044     * @param layer The data layer. Must not be {@code null}
045     * @param osm The primitive to add
046     */
047    public AddCommand(OsmDataLayer layer, OsmPrimitive osm) {
048        super(layer);
049        this.osm = Objects.requireNonNull(osm, "osm");
050    }
051
052    protected static final void checkNodeStyles(OsmPrimitive osm) {
053        if (osm instanceof Way) {
054            // Fix #10557 - node icon not updated after undoing/redoing addition of a way
055            ((Way) osm).clearCachedNodeStyles();
056        }
057    }
058
059    @Override
060    public boolean executeCommand() {
061        getAffectedDataSet().addPrimitive(osm);
062        osm.setModified(true);
063        checkNodeStyles(osm);
064        return true;
065    }
066
067    @Override
068    public void undoCommand() {
069        getAffectedDataSet().removePrimitive(osm);
070        checkNodeStyles(osm);
071    }
072
073    @Override
074    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
075        added.add(osm);
076    }
077
078    @Override
079    public String getDescriptionText() {
080        String msg;
081        switch(OsmPrimitiveType.from(osm)) {
082        case NODE: msg = marktr("Add node {0}"); break;
083        case WAY: msg = marktr("Add way {0}"); break;
084        case RELATION: msg = marktr("Add relation {0}"); break;
085        default: /* should not happen */msg = ""; break;
086        }
087        return tr(msg, osm.getDisplayName(DefaultNameFormatter.getInstance()));
088    }
089
090    @Override
091    public Icon getDescriptionIcon() {
092        return ImageProvider.get(osm.getDisplayType());
093    }
094
095    @Override
096    public Collection<OsmPrimitive> getParticipatingPrimitives() {
097        return Collections.singleton(osm);
098    }
099
100    @Override
101    public int hashCode() {
102        return Objects.hash(super.hashCode(), osm);
103    }
104
105    @Override
106    public boolean equals(Object obj) {
107        if (this == obj) return true;
108        if (obj == null || getClass() != obj.getClass()) return false;
109        if (!super.equals(obj)) return false;
110        AddCommand that = (AddCommand) obj;
111        return Objects.equals(osm, that.osm);
112    }
113}