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