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

Last change on this file since 9171 was 8975, checked in by simon04, 8 years ago

fix #12041 - Wrong message in relation member deletion confirmation

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