source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java@ 2502

Last change on this file since 2502 was 2502, checked in by Gubaer, 14 years ago

fixed #4007: Deleting/removing value of a key does not remove the key, but should

File size: 13.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging;
3
4import static org.openstreetmap.josm.tools.I18n.trn;
5
6import java.beans.PropertyChangeListener;
7import java.beans.PropertyChangeSupport;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.Comparator;
11import java.util.HashMap;
12import java.util.Iterator;
13import java.util.List;
14import java.util.Map;
15import java.util.logging.Logger;
16
17import javax.swing.table.AbstractTableModel;
18
19import org.openstreetmap.josm.command.ChangePropertyCommand;
20import org.openstreetmap.josm.command.Command;
21import org.openstreetmap.josm.command.SequenceCommand;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Tagged;
24
25
26/**
27 * TagEditorModel is a table model.
28 *
29 *
30 * @author gubaer
31 *
32 */
33@SuppressWarnings("serial")
34public class TagEditorModel extends AbstractTableModel {
35 static private final Logger logger = Logger.getLogger(TagEditorModel.class.getName());
36
37 static public final String PROP_DIRTY = TagEditorModel.class.getName() + ".dirty";
38
39 /** the list holding the tags */
40 private ArrayList<TagModel> tags = null;
41
42 /** indicates whether the model is dirty */
43 private boolean dirty = false;
44 private PropertyChangeSupport propChangeSupport = null;
45
46 /**
47 * constructor
48 */
49 public TagEditorModel(){
50 tags = new ArrayList<TagModel>();
51 propChangeSupport = new PropertyChangeSupport(this);
52 }
53
54 public void addPropertyChangeListener(PropertyChangeListener listener) {
55 propChangeSupport.addPropertyChangeListener(listener);
56 }
57
58 public void removeProperyChangeListener(PropertyChangeListener listener) {
59 propChangeSupport.removePropertyChangeListener(listener);
60 }
61
62 protected void fireDirtyStateChanged(final boolean oldValue, final boolean newValue) {
63 propChangeSupport.firePropertyChange(PROP_DIRTY, oldValue, newValue);
64 }
65
66 protected void setDirty(boolean newValue) {
67 boolean oldValue = dirty;
68 dirty = newValue;
69 if (oldValue != newValue) {
70 fireDirtyStateChanged(oldValue, newValue);
71 }
72 }
73
74 public int getColumnCount() {
75 return 2;
76 }
77
78 public int getRowCount() {
79 return tags.size();
80 }
81
82 public Object getValueAt(int rowIndex, int columnIndex) {
83 if (rowIndex >= getRowCount())
84 throw new IndexOutOfBoundsException("unexpected rowIndex: rowIndex=" + rowIndex);
85
86 TagModel tag = tags.get(rowIndex);
87 switch(columnIndex) {
88 case 0:
89 case 1: return tag;
90
91 default:
92 throw new IndexOutOfBoundsException("unexpected columnIndex: columnIndex=" + columnIndex);
93 }
94 }
95
96 /**
97 * removes all tags in the model
98 */
99 public void clear() {
100 tags.clear();
101 setDirty(true);
102 fireTableDataChanged();
103 }
104
105 /**
106 * adds a tag to the model
107 *
108 * @param tag the tag. Must not be null.
109 *
110 * @exception IllegalArgumentException thrown, if tag is null
111 */
112 public void add(TagModel tag) {
113 if (tag == null)
114 throw new IllegalArgumentException("argument 'tag' must not be null");
115 tags.add(tag);
116 setDirty(true);
117 fireTableDataChanged();
118 }
119
120
121 public void prepend(TagModel tag) {
122 if (tag == null)
123 throw new IllegalArgumentException("argument 'tag' must not be null");
124 tags.add(0, tag);
125 setDirty(true);
126 fireTableDataChanged();
127 }
128
129
130 /**
131 * adds a tag given by a name/value pair to the tag editor model.
132 *
133 * If there is no tag with name <code>name</name> yet, a new {@link TagModel} is created
134 * and append to this model.
135 *
136 * If there is a tag with name <code>name</name>, <code>value</code> is merged to the list
137 * of values for this tag.
138 *
139 * @param name the name; converted to "" if null
140 * @param value the value; converted to "" if null
141 */
142 public void add(String name, String value) {
143 name = (name == null) ? "" : name;
144 value = (value == null) ? "" : value;
145
146 TagModel tag = get(name);
147 if (tag == null) {
148 tag = new TagModel(name, value);
149 add(tag);
150 } else {
151 tag.addValue(value);
152 }
153 setDirty(true);
154 }
155
156
157 /**
158 * replies the tag with name <code>name</code>; null, if no such tag exists
159 * @param name the tag name
160 * @return the tag with name <code>name</code>; null, if no such tag exists
161 */
162 public TagModel get(String name) {
163 name = (name == null) ? "" : name;
164 for (TagModel tag : tags) {
165 if (tag.getName().equals(name))
166 return tag;
167 }
168 return null;
169 }
170
171 public TagModel get(int idx) {
172 TagModel tagModel = tags.get(idx);
173 return tagModel;
174 }
175
176
177
178 @Override public boolean isCellEditable(int row, int col) {
179 // all cells are editable
180 return true;
181 }
182
183
184 /**
185 * deletes the names of the tags given by tagIndices
186 *
187 * @param tagIndices a list of tag indices
188 */
189 public void deleteTagNames(int [] tagIndices) {
190 if (tags == null)
191 return;
192 for (int tagIdx : tagIndices) {
193 TagModel tag = tags.get(tagIdx);
194 if (tag != null) {
195 tag.setName("");
196 }
197 }
198 fireTableDataChanged();
199 setDirty(true);
200 }
201
202 /**
203 * deletes the values of the tags given by tagIndices
204 *
205 * @param tagIndices the lit of tag indices
206 */
207 public void deleteTagValues(int [] tagIndices) {
208 if (tags == null)
209 return;
210 for (int tagIdx : tagIndices) {
211 TagModel tag = tags.get(tagIdx);
212 if (tag != null) {
213 tag.setValue("");
214 }
215 }
216 fireTableDataChanged();
217 setDirty(true);
218 }
219
220 /**
221 * Deletes all tags with name <code>name</code>
222 *
223 * @param name the name. Ignored if null.
224 */
225 public void delete(String name) {
226 if (name == null) return;
227 Iterator<TagModel> it = tags.iterator();
228 while(it.hasNext()) {
229 TagModel tm = it.next();
230 if (tm.getName().equals(name)) {
231 it.remove();
232 }
233 }
234 fireTableDataChanged();
235 setDirty(true);
236 }
237 /**
238 * deletes the tags given by tagIndices
239 *
240 * @param tagIndices the list of tag indices
241 */
242 public void deleteTags(int [] tagIndices) {
243 if (tags == null)
244 return;
245 ArrayList<TagModel> toDelete = new ArrayList<TagModel>();
246 for (int tagIdx : tagIndices) {
247 TagModel tag = tags.get(tagIdx);
248 if (tag != null) {
249 toDelete.add(tag);
250 }
251 }
252 for (TagModel tag : toDelete) {
253 tags.remove(tag);
254 }
255 fireTableDataChanged();
256 setDirty(true);
257 }
258
259
260 /**
261 * creates a new tag and appends it to the model
262 */
263 public void appendNewTag() {
264 TagModel tag = new TagModel();
265 tags.add(tag);
266 fireTableDataChanged();
267 setDirty(true);
268 }
269
270 /**
271 * makes sure the model includes at least one (empty) tag
272 */
273 public void ensureOneTag() {
274 if (tags.size() == 0) {
275 appendNewTag();
276 }
277 }
278
279 /**
280 * initializes the model with the tags of an OSM primitive
281 *
282 * @param primitive the OSM primitive
283 */
284 public void initFromPrimitive(Tagged primitive) {
285 clear();
286 for (String key : primitive.keySet()) {
287 String value = primitive.get(key);
288 add(key,value);
289 }
290 TagModel tag = new TagModel();
291 sort();
292 tags.add(tag);
293 setDirty(false);
294 }
295
296 /**
297 * initializes the model with the tags of an OSM primitive
298 *
299 * @param primitive the OSM primitive
300 */
301 public void initFromTags(Map<String,String> tags) {
302 clear();
303 for (String key : tags.keySet()) {
304 String value = tags.get(key);
305 add(key,value);
306 }
307 TagModel tag = new TagModel();
308 sort();
309 this.tags.add(tag);
310 setDirty(false);
311 }
312
313 /**
314 * applies the current state of the tag editor model to a primitive
315 *
316 * @param primitive the primitive
317 *
318 */
319 public void applyToPrimitive(Tagged primitive) {
320 Map<String,String> tags = primitive.getKeys();
321 applyToTags(tags);
322 primitive.setKeys(tags);
323 }
324
325 /**
326 * applies the current state of the tag editor model to a map of tags
327 *
328 * @param tags the map of key/value pairs
329 *
330 */
331 public void applyToTags(Map<String, String> tags) {
332 tags.clear();
333 for (TagModel tag: this.tags) {
334 // tag still holds an unchanged list of different values for the same key.
335 // no property change command required
336 if (tag.getValueCount() > 1) {
337 continue;
338 }
339
340 // tag name holds an empty key. Don't apply it to the selection.
341 //
342 if (tag.getName().trim().equals("")) {
343 continue;
344 }
345 // tag has an empty value => don't apply the tag
346 //
347 if (tag.getValue().trim().equals("")) {
348 continue;
349 }
350 tags.put(tag.getName(), tag.getValue());
351 }
352 }
353
354 public Map<String,String> getTags() {
355 Map<String,String> tags = new HashMap<String, String>();
356 applyToTags(tags);
357 return tags;
358 }
359
360 /**
361 * checks whether the tag model includes a tag with a given key
362 *
363 * @param key the key
364 * @return true, if the tag model includes the tag; false, otherwise
365 */
366 public boolean includesTag(String key) {
367 if (key == null) return false;
368 for (TagModel tag : tags) {
369 if (tag.getName().equals(key))
370 return true;
371 }
372 return false;
373 }
374
375
376 protected Command createUpdateTagCommand(Collection<OsmPrimitive> primitives, TagModel tag) {
377
378 // tag still holds an unchanged list of different values for the same key.
379 // no property change command required
380 if (tag.getValueCount() > 1)
381 return null;
382
383 // tag name holds an empty key. Don't apply it to the selection.
384 //
385 if (tag.getName().trim().equals(""))
386 return null;
387
388 String newkey = tag.getName();
389 String newvalue = tag.getValue();
390
391 ChangePropertyCommand command = new ChangePropertyCommand(primitives,newkey, newvalue);
392 return command;
393 }
394
395 protected Command createDeleteTagsCommand(Collection<OsmPrimitive> primitives) {
396
397 List<String> currentkeys = getKeys();
398 ArrayList<Command> commands = new ArrayList<Command>();
399
400 for (OsmPrimitive primitive : primitives) {
401 for (String oldkey : primitive.keySet()) {
402 if (!currentkeys.contains(oldkey)) {
403 ChangePropertyCommand deleteCommand =
404 new ChangePropertyCommand(primitive,oldkey,null);
405 commands.add(deleteCommand);
406 }
407 }
408 }
409
410 SequenceCommand command = new SequenceCommand(
411 trn("Remove old keys from up to {0} object", "Remove old keys from up to {0} objects", primitives.size(), primitives.size()),
412 commands
413 );
414
415 return command;
416 }
417
418 /**
419 * replies the list of keys of the tags managed by this model
420 *
421 * @return the list of keys managed by this model
422 */
423 public List<String> getKeys() {
424 ArrayList<String> keys = new ArrayList<String>();
425 for (TagModel tag: tags) {
426 if (!tag.getName().trim().equals("")) {
427 keys.add(tag.getName());
428 }
429 }
430 return keys;
431 }
432
433 /**
434 * sorts the current tags according alphabetical order of names
435 */
436 protected void sort() {
437 java.util.Collections.sort(
438 tags,
439 new Comparator<TagModel>() {
440 public int compare(TagModel self, TagModel other) {
441 return self.getName().compareTo(other.getName());
442 }
443 }
444 );
445 }
446
447 /**
448 * updates the name of a tag and sets the dirty state to true if
449 * the new name is different from the old name.
450 *
451 * @param tag the tag
452 * @param newName the new name
453 */
454 public void updateTagName(TagModel tag, String newName) {
455 String oldName = tag.getName();
456 tag.setName(newName);
457 if (! newName.equals(oldName)) {
458 setDirty(true);
459 }
460 }
461
462 /**
463 * updates the value value of a tag and sets the dirty state to true if the
464 * new name is different from the old name
465 *
466 * @param tag the tag
467 * @param newValue the new value
468 */
469 public void updateTagValue(TagModel tag, String newValue) {
470 String oldValue = tag.getValue();
471 tag.setValue(newValue);
472 if (! newValue.equals(oldValue)) {
473 setDirty(true);
474 }
475 }
476
477 /**
478 * replies true, if this model has been updated
479 *
480 * @return true, if this model has been updated
481 */
482 public boolean isDirty() {
483 return dirty;
484 }
485}
Note: See TracBrowser for help on using the repository browser.