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

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

fix #13223 - Minor command class fixes (patch by michael2402, modified) - gsoc-core

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