source: josm/trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java@ 12167

Last change on this file since 12167 was 11348, checked in by Don-vip, 8 years ago

findbugs - fix some SIC_INNER_SHOULD_BE_STATIC_ANON

  • Property svn:eol-style set to native
File size: 10.0 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[626]2package org.openstreetmap.josm.command;
3
[1990]4import static org.openstreetmap.josm.tools.I18n.marktr;
[626]5import static org.openstreetmap.josm.tools.I18n.tr;
[6507]6import static org.openstreetmap.josm.tools.I18n.trn;
[626]7
[4302]8import java.util.Arrays;
[626]9import java.util.Collection;
[3262]10import java.util.Collections;
[4773]11import java.util.HashMap;
[626]12import java.util.LinkedList;
13import java.util.List;
[4773]14import java.util.Map;
[9371]15import java.util.Objects;
[11348]16import java.util.stream.Collectors;
[5077]17
[4918]18import javax.swing.Icon;
[626]19
[8941]20import org.openstreetmap.josm.data.osm.DataSet;
[626]21import org.openstreetmap.josm.data.osm.OsmPrimitive;
[1814]22import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
[1990]23import org.openstreetmap.josm.gui.DefaultNameFormatter;
[8975]24import org.openstreetmap.josm.tools.I18n;
[626]25import org.openstreetmap.josm.tools.ImageProvider;
26
27/**
28 * Command that manipulate the key/value structure of several objects. Manages deletion,
29 * adding and modify of values and keys.
[1169]30 *
[626]31 * @author imi
[8945]32 * @since 24
[626]33 */
34public class ChangePropertyCommand extends Command {
[11348]35
36 static final class OsmPseudoCommand implements PseudoCommand {
37 private final OsmPrimitive osm;
38
39 OsmPseudoCommand(OsmPrimitive osm) {
40 this.osm = osm;
41 }
42
43 @Override
44 public String getDescriptionText() {
45 return osm.getDisplayName(DefaultNameFormatter.getInstance());
46 }
47
48 @Override
49 public Icon getDescriptionIcon() {
50 return ImageProvider.get(osm.getDisplayType());
51 }
52
53 @Override
54 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
55 return Collections.singleton(osm);
56 }
57 }
58
[1169]59 /**
60 * All primitives that are affected with this command.
61 */
[8945]62 private final List<OsmPrimitive> objects = new LinkedList<>();
63
[1169]64 /**
[4773]65 * Key and value pairs. If value is <code>null</code>, delete all key references with the given
[6324]66 * key. Otherwise, change the tags of all objects to the given value or create keys of
[1169]67 * those objects that do not have the key yet.
68 */
[8338]69 private final Map<String, String> tags;
[628]70
[4773]71 /**
[6324]72 * Creates a command to change multiple tags of multiple objects
[4773]73 *
74 * @param objects the objects to modify
[6324]75 * @param tags the tags to set
[4773]76 */
[8338]77 public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, Map<String, String> tags) {
[4773]78 this.tags = tags;
79 init(objects);
[1169]80 }
[626]81
[4773]82 /**
[6324]83 * Creates a command to change one tag of multiple objects
[4773]84 *
85 * @param objects the objects to modify
[6324]86 * @param key the key of the tag to set
[4773]87 * @param value the value of the key to set
88 */
89 public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) {
[7005]90 this.tags = new HashMap<>(1);
[4773]91 this.tags.put(key, value);
92 init(objects);
93 }
94
95 /**
[6324]96 * Creates a command to change one tag of one object
[4773]97 *
98 * @param object the object to modify
[6324]99 * @param key the key of the tag to set
[4773]100 * @param value the value of the key to set
101 */
[1169]102 public ChangePropertyCommand(OsmPrimitive object, String key, String value) {
[4302]103 this(Arrays.asList(object), key, value);
[1169]104 }
[626]105
[4773]106 /**
107 * Initialize the instance by finding what objects will be modified
108 *
109 * @param objects the objects to (possibly) modify
110 */
111 private void init(Collection<? extends OsmPrimitive> objects) {
112 // determine what objects will be modified
113 for (OsmPrimitive osm : objects) {
114 boolean modified = false;
115
116 // loop over all tags
117 for (Map.Entry<String, String> tag : this.tags.entrySet()) {
118 String oldVal = osm.get(tag.getKey());
119 String newVal = tag.getValue();
120
121 if (newVal == null || newVal.isEmpty()) {
122 if (oldVal != null)
123 // new value is null and tag exists (will delete tag)
124 modified = true;
[8342]125 } else if (oldVal == null || !newVal.equals(oldVal))
[4773]126 // new value is not null and is different from current value
127 modified = true;
128 }
129 if (modified)
130 this.objects.add(osm);
131 }
132 }
133
[8941]134 @Override
135 public boolean executeCommand() {
[8945]136 if (objects.isEmpty())
137 return true;
[8941]138 final DataSet dataSet = objects.get(0).getDataSet();
[8961]139 if (dataSet != null) {
140 dataSet.beginUpdate();
141 }
[3910]142 try {
143 super.executeCommand(); // save old
[4773]144
145 for (OsmPrimitive osm : objects) {
[5143]146 // loop over all tags
[4773]147 for (Map.Entry<String, String> tag : this.tags.entrySet()) {
148 String oldVal = osm.get(tag.getKey());
149 String newVal = tag.getValue();
150
151 if (newVal == null || newVal.isEmpty()) {
[5143]152 if (oldVal != null)
[4773]153 osm.remove(tag.getKey());
[8342]154 } else if (oldVal == null || !newVal.equals(oldVal))
[4773]155 osm.put(tag.getKey(), newVal);
[3910]156 }
[5143]157 // init() only keeps modified primitives. Therefore the modified
158 // bit can be set without further checks.
159 osm.setModified(true);
[1169]160 }
[3910]161 return true;
[8342]162 } finally {
[8961]163 if (dataSet != null) {
164 dataSet.endUpdate();
165 }
[3910]166 }
[626]167 }
[1169]168
[8941]169 @Override
170 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
[1169]171 modified.addAll(objects);
172 }
173
[4918]174 @Override
175 public String getDescriptionText() {
[8975]176 @I18n.QuirkyPluralString
177 final String text;
[4773]178 if (objects.size() == 1 && tags.size() == 1) {
[8846]179 OsmPrimitive primitive = objects.get(0);
[10308]180 String msg;
[4773]181 Map.Entry<String, String> entry = tags.entrySet().iterator().next();
[10663]182 if (entry.getValue() == null || entry.getValue().isEmpty()) {
[1989]183 switch(OsmPrimitiveType.from(primitive)) {
[2990]184 case NODE: msg = marktr("Remove \"{0}\" for node ''{1}''"); break;
185 case WAY: msg = marktr("Remove \"{0}\" for way ''{1}''"); break;
186 case RELATION: msg = marktr("Remove \"{0}\" for relation ''{1}''"); break;
[10216]187 default: throw new AssertionError();
[1989]188 }
[4773]189 text = tr(msg, entry.getKey(), primitive.getDisplayName(DefaultNameFormatter.getInstance()));
[1989]190 } else {
191 switch(OsmPrimitiveType.from(primitive)) {
[2990]192 case NODE: msg = marktr("Set {0}={1} for node ''{2}''"); break;
193 case WAY: msg = marktr("Set {0}={1} for way ''{2}''"); break;
194 case RELATION: msg = marktr("Set {0}={1} for relation ''{2}''"); break;
[10216]195 default: throw new AssertionError();
[1989]196 }
[4773]197 text = tr(msg, entry.getKey(), entry.getValue(), primitive.getDisplayName(DefaultNameFormatter.getInstance()));
[1989]198 }
[4773]199 } else if (objects.size() > 1 && tags.size() == 1) {
200 Map.Entry<String, String> entry = tags.entrySet().iterator().next();
[10663]201 if (entry.getValue() == null || entry.getValue().isEmpty()) {
[6679]202 /* I18n: plural form for objects, but value < 2 not possible! */
203 text = trn("Remove \"{0}\" for {1} object", "Remove \"{0}\" for {1} objects", objects.size(), entry.getKey(), objects.size());
[6507]204 } else {
[6679]205 /* I18n: plural form for objects, but value < 2 not possible! */
[8509]206 text = trn("Set {0}={1} for {2} object", "Set {0}={1} for {2} objects",
207 objects.size(), entry.getKey(), entry.getValue(), objects.size());
[6507]208 }
[8342]209 } else {
[4773]210 boolean allnull = true;
211 for (Map.Entry<String, String> tag : this.tags.entrySet()) {
[10663]212 if (tag.getValue() != null && !tag.getValue().isEmpty()) {
[4773]213 allnull = false;
214 break;
215 }
216 }
[5077]217
[4773]218 if (allnull) {
[6881]219 /* I18n: plural form detected for objects only (but value < 2 not possible!), try to do your best for tags */
[6679]220 text = trn("Deleted {0} tags for {1} object", "Deleted {0} tags for {1} objects", objects.size(), tags.size(), objects.size());
[6507]221 } else {
[6679]222 /* I18n: plural form detected for objects only (but value < 2 not possible!), try to do your best for tags */
223 text = trn("Set {0} tags for {1} object", "Set {0} tags for {1} objects", objects.size(), tags.size(), objects.size());
[6507]224 }
[4773]225 }
[4918]226 return text;
[3262]227 }
228
[4918]229 @Override
230 public Icon getDescriptionIcon() {
231 return ImageProvider.get("data", "key");
232 }
233
[8945]234 @Override
235 public Collection<PseudoCommand> getChildren() {
[1169]236 if (objects.size() == 1)
[3262]237 return null;
[11348]238 return objects.stream().map(OsmPseudoCommand::new).collect(Collectors.toList());
[1169]239 }
[6538]240
[6881]241 /**
[8945]242 * Returns the number of objects that will effectively be modified, before the command is executed.
243 * @return the number of objects that will effectively be modified (can be 0)
244 * @see Command#getParticipatingPrimitives()
245 * @since 8945
246 */
247 public final int getObjectsNumber() {
248 return objects.size();
249 }
250
251 /**
[6881]252 * Returns the tags to set (key/value pairs).
253 * @return the tags to set (key/value pairs)
254 */
[6538]255 public Map<String, String> getTags() {
256 return Collections.unmodifiableMap(tags);
257 }
[8456]258
259 @Override
260 public int hashCode() {
[9371]261 return Objects.hash(super.hashCode(), objects, tags);
[8456]262 }
263
264 @Override
265 public boolean equals(Object obj) {
[9371]266 if (this == obj) return true;
267 if (obj == null || getClass() != obj.getClass()) return false;
268 if (!super.equals(obj)) return false;
269 ChangePropertyCommand that = (ChangePropertyCommand) obj;
270 return Objects.equals(objects, that.objects) &&
271 Objects.equals(tags, that.tags);
[8456]272 }
[626]273}
Note: See TracBrowser for help on using the repository browser.