001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.command; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.util.ArrayList; 008import java.util.Collection; 009import java.util.Collections; 010import java.util.LinkedList; 011import java.util.List; 012import java.util.NoSuchElementException; 013import java.util.Objects; 014 015import javax.swing.Icon; 016 017import org.openstreetmap.josm.data.osm.DataSet; 018import org.openstreetmap.josm.data.osm.OsmPrimitive; 019import org.openstreetmap.josm.data.validation.util.NameVisitor; 020import org.openstreetmap.josm.tools.ImageProvider; 021 022/** 023 * Command that replaces the key of one or several objects 024 * @since 3669 025 */ 026public class ChangePropertyKeyCommand extends Command { 027 static final class SinglePrimitivePseudoCommand implements PseudoCommand { 028 private final String name; 029 private final OsmPrimitive osm; 030 private final Icon icon; 031 032 SinglePrimitivePseudoCommand(String name, OsmPrimitive osm, Icon icon) { 033 this.name = name; 034 this.osm = osm; 035 this.icon = icon; 036 } 037 038 @Override 039 public String getDescriptionText() { 040 return name; 041 } 042 043 @Override 044 public Icon getDescriptionIcon() { 045 return icon; 046 } 047 048 @Override 049 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 050 return Collections.singleton(osm); 051 } 052 } 053 054 /** 055 * All primitives, that are affected with this command. 056 */ 057 private final List<? extends OsmPrimitive> objects; 058 /** 059 * The key that is subject to change. 060 */ 061 private final String key; 062 /** 063 * The mew key. 064 */ 065 private final String newKey; 066 067 /** 068 * Constructs a new {@code ChangePropertyKeyCommand}. 069 * 070 * @param object the object subject to change replacement. Must not be null, and belong to a data set 071 * @param key The key to replace 072 * @param newKey the new value of the key 073 * @since 6329 074 */ 075 public ChangePropertyKeyCommand(OsmPrimitive object, String key, String newKey) { 076 this(Collections.singleton(object), key, newKey); 077 } 078 079 /** 080 * Constructs a new {@code ChangePropertyKeyCommand}. 081 * 082 * @param objects all objects subject to change replacement. Must not be null or empty, and objects must belong to a data set 083 * @param key The key to replace 084 * @param newKey the new value of the key 085 * @throws NullPointerException if objects is null or contain null item 086 * @throws NoSuchElementException if objects is empty 087 */ 088 public ChangePropertyKeyCommand(Collection<? extends OsmPrimitive> objects, String key, String newKey) { 089 this(objects.iterator().next().getDataSet(), objects, key, newKey); 090 } 091 092 /** 093 * Constructs a new {@code ChangePropertyKeyCommand}. 094 * 095 * @param ds The target data set. Must not be {@code null} 096 * @param objects all objects subject to change replacement. 097 * @param key The key to replace 098 * @param newKey the new value of the key 099 * @since 12726 100 */ 101 public ChangePropertyKeyCommand(DataSet ds, Collection<? extends OsmPrimitive> objects, String key, String newKey) { 102 super(ds); 103 this.objects = new LinkedList<>(objects); 104 this.key = key; 105 this.newKey = newKey; 106 } 107 108 @Override 109 public boolean executeCommand() { 110 if (!super.executeCommand()) 111 return false; // save old 112 for (OsmPrimitive osm : objects) { 113 String oldValue = osm.get(key); 114 if (oldValue != null || osm.hasKey(newKey)) { 115 osm.setModified(true); 116 osm.put(newKey, oldValue); 117 osm.remove(key); 118 } 119 } 120 return true; 121 } 122 123 @Override 124 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 125 modified.addAll(objects); 126 } 127 128 @Override 129 public String getDescriptionText() { 130 String text = tr("Replace \"{0}\" by \"{1}\" for", key, newKey); 131 if (objects.size() == 1) { 132 NameVisitor v = new NameVisitor(); 133 objects.get(0).accept(v); 134 text += " "+tr(v.className)+" "+v.name; 135 } else { 136 text += " "+objects.size()+" "+trn("object", "objects", objects.size()); 137 } 138 return text; 139 } 140 141 @Override 142 public Icon getDescriptionIcon() { 143 return ImageProvider.get("dialogs", "propertiesdialog", ImageProvider.ImageSizes.SMALLICON); 144 } 145 146 @Override 147 public Collection<PseudoCommand> getChildren() { 148 if (objects.size() == 1) 149 return null; 150 List<PseudoCommand> children = new ArrayList<>(); 151 152 final NameVisitor v = new NameVisitor(); 153 for (final OsmPrimitive osm : objects) { 154 osm.accept(v); 155 children.add(new SinglePrimitivePseudoCommand(v.name, osm, v.icon)); 156 } 157 return children; 158 } 159 160 @Override 161 public int hashCode() { 162 return Objects.hash(super.hashCode(), objects, key, newKey); 163 } 164 165 @Override 166 public boolean equals(Object obj) { 167 if (this == obj) return true; 168 if (obj == null || getClass() != obj.getClass()) return false; 169 if (!super.equals(obj)) return false; 170 ChangePropertyKeyCommand that = (ChangePropertyKeyCommand) obj; 171 return Objects.equals(objects, that.objects) && 172 Objects.equals(key, that.key) && 173 Objects.equals(newKey, that.newKey); 174 } 175}