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.OsmPrimitive; 013import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 014import org.openstreetmap.josm.data.osm.Way; 015import org.openstreetmap.josm.gui.DefaultNameFormatter; 016import org.openstreetmap.josm.gui.layer.OsmDataLayer; 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 the current edit layer, if any. 032 * @param osm The existing primitive to modify 033 * @param newOsm The new primitive 034 */ 035 public ChangeCommand(OsmPrimitive osm, OsmPrimitive newOsm) { 036 this.osm = osm; 037 this.newOsm = newOsm; 038 sanityChecks(); 039 } 040 041 /** 042 * Constructs a new {@code ChangeCommand} in the context of a given data layer. 043 * @param layer The data layer 044 * @param osm The existing primitive to modify 045 * @param newOsm The new primitive 046 */ 047 public ChangeCommand(OsmDataLayer layer, OsmPrimitive osm, OsmPrimitive newOsm) { 048 super(layer); 049 this.osm = osm; 050 this.newOsm = newOsm; 051 sanityChecks(); 052 } 053 054 private void sanityChecks() { 055 CheckParameterUtil.ensureParameterNotNull(osm, "osm"); 056 CheckParameterUtil.ensureParameterNotNull(newOsm, "newOsm"); 057 if (newOsm instanceof Way && ((Way) newOsm).getNodesCount() == 0) { 058 // Do not allow to create empty ways (see #7465) 059 throw new IllegalArgumentException(tr("New way {0} has 0 nodes", newOsm)); 060 } 061 } 062 063 @Override 064 public boolean executeCommand() { 065 super.executeCommand(); 066 osm.cloneFrom(newOsm); 067 osm.setModified(true); 068 return true; 069 } 070 071 @Override 072 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 073 modified.add(osm); 074 } 075 076 @Override 077 public String getDescriptionText() { 078 String msg; 079 switch(OsmPrimitiveType.from(osm)) { 080 case NODE: msg = marktr("Change node {0}"); break; 081 case WAY: msg = marktr("Change way {0}"); break; 082 case RELATION: msg = marktr("Change relation {0}"); break; 083 default: throw new AssertionError(); 084 } 085 return tr(msg, osm.getDisplayName(DefaultNameFormatter.getInstance())); 086 } 087 088 @Override 089 public Icon getDescriptionIcon() { 090 return ImageProvider.get(osm.getDisplayType()); 091 } 092 093 @Override 094 public int hashCode() { 095 return Objects.hash(super.hashCode(), osm, newOsm); 096 } 097 098 @Override 099 public boolean equals(Object obj) { 100 if (this == obj) return true; 101 if (obj == null || getClass() != obj.getClass()) return false; 102 if (!super.equals(obj)) return false; 103 ChangeCommand that = (ChangeCommand) obj; 104 return Objects.equals(osm, that.osm) && 105 Objects.equals(newOsm, that.newOsm); 106 } 107}