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.Objects;
009
010import javax.swing.Icon;
011
012import org.openstreetmap.josm.data.osm.DataSet;
013import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
014import org.openstreetmap.josm.data.osm.OsmPrimitive;
015import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
016import org.openstreetmap.josm.data.osm.Way;
017import org.openstreetmap.josm.tools.CheckParameterUtil;
018import org.openstreetmap.josm.tools.ImageProvider;
019
020/**
021 * Command that basically replaces one OSM primitive by another of the same type.
022 *
023 * @since 93
024 */
025public class ChangeCommand extends Command {
026
027    private final OsmPrimitive osm;
028    private final OsmPrimitive newOsm;
029
030    /**
031     * Constructs a new {@code ChangeCommand} in the context of {@code osm} data set.
032     * @param osm The existing primitive to modify. It must belong to a data set
033     * @param newOsm The new primitive
034     */
035    public ChangeCommand(OsmPrimitive osm, OsmPrimitive newOsm) {
036        this(osm.getDataSet(), osm, newOsm);
037    }
038
039    /**
040     * Constructs a new {@code ChangeCommand} in the context of a given data set.
041     * @param data The data set
042     * @param osm The existing primitive to modify
043     * @param newOsm The new primitive
044     * @since 11240
045     */
046    public ChangeCommand(DataSet data, OsmPrimitive osm, OsmPrimitive newOsm) {
047        super(data);
048        this.osm = osm;
049        this.newOsm = newOsm;
050        sanityChecks();
051    }
052
053    private void sanityChecks() {
054        CheckParameterUtil.ensureParameterNotNull(osm, "osm");
055        CheckParameterUtil.ensureParameterNotNull(newOsm, "newOsm");
056        if (newOsm instanceof Way && ((Way) newOsm).getNodesCount() == 0) {
057            // Do not allow to create empty ways (see #7465)
058            throw new IllegalArgumentException(tr("New way {0} has 0 nodes", newOsm));
059        }
060    }
061
062    @Override
063    public boolean executeCommand() {
064        super.executeCommand();
065        osm.cloneFrom(newOsm);
066        osm.setModified(true);
067        return true;
068    }
069
070    @Override
071    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
072        modified.add(osm);
073    }
074
075    @Override
076    public String getDescriptionText() {
077        String msg;
078        switch(OsmPrimitiveType.from(osm)) {
079        case NODE: msg = marktr("Change node {0}"); break;
080        case WAY: msg = marktr("Change way {0}"); break;
081        case RELATION: msg = marktr("Change relation {0}"); break;
082        default: throw new AssertionError();
083        }
084        return tr(msg, osm.getDisplayName(DefaultNameFormatter.getInstance()));
085    }
086
087    @Override
088    public Icon getDescriptionIcon() {
089        return ImageProvider.get(osm.getDisplayType());
090    }
091
092    /**
093     * Returns the original OSM primitive to modify. It belongs to a dataset.
094     * @return the original OSM primitive to modify
095     * @since 14283
096     */
097    public final OsmPrimitive getOsmPrimitive() {
098        return osm;
099    }
100
101    /**
102     * Returns the new OSM primitive.
103     * @return the new OSM primitive
104     * @since 14283
105     */
106    public final OsmPrimitive getNewOsmPrimitive() {
107        return newOsm;
108    }
109
110    @Override
111    public int hashCode() {
112        return Objects.hash(super.hashCode(), osm, newOsm);
113    }
114
115    @Override
116    public boolean equals(Object obj) {
117        if (this == obj) return true;
118        if (obj == null || getClass() != obj.getClass()) return false;
119        if (!super.equals(obj)) return false;
120        ChangeCommand that = (ChangeCommand) obj;
121        return Objects.equals(osm, that.osm) &&
122                Objects.equals(newOsm, that.newOsm);
123    }
124}