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 } 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 @Override 093 public int hashCode() { 094 return Objects.hash(super.hashCode(), osm, newOsm); 095 } 096 097 @Override 098 public boolean equals(Object obj) { 099 if (this == obj) return true; 100 if (obj == null || getClass() != obj.getClass()) return false; 101 if (!super.equals(obj)) return false; 102 ChangeCommand that = (ChangeCommand) obj; 103 return Objects.equals(osm, that.osm) && 104 Objects.equals(newOsm, that.newOsm); 105 } 106}