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

Last change on this file since 5543 was 5266, checked in by bastiK, 12 years ago

fixed majority of javadoc warnings by replacing "{@see" by "{@link"

  • Property svn:eol-style set to native
File size: 18.2 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;
15
16import javax.swing.DefaultListSelectionModel;
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.TagCollection;
24import org.openstreetmap.josm.data.osm.Tagged;
25import org.openstreetmap.josm.tools.CheckParameterUtil;
26
27/**
28 * TagEditorModel is a table model.
29 *
30 */
31@SuppressWarnings("serial")
32public class TagEditorModel extends AbstractTableModel {
33 static public final String PROP_DIRTY = TagEditorModel.class.getName() + ".dirty";
34
35 /** the list holding the tags */
36 protected final ArrayList<TagModel> tags =new ArrayList<TagModel>();
37
38 /** indicates whether the model is dirty */
39 private boolean dirty = false;
40 private final PropertyChangeSupport propChangeSupport = new PropertyChangeSupport(this);
41
42 private DefaultListSelectionModel rowSelectionModel;
43 private DefaultListSelectionModel colSelectionModel;
44
45 /**
46 * Creates a new tag editor model. Internally allocates two selection models
47 * for row selection and column selection.
48 *
49 * To create a {@link JTable} with this model:
50 * <pre>
51 * TagEditorModel model = new TagEditorModel();
52 * TagTable tbl = new TagTabel(model);
53 * </pre>
54 *
55 * @see #getRowSelectionModel()
56 * @see #getColumnSelectionModel()
57 */
58 public TagEditorModel() {
59 this.rowSelectionModel = new DefaultListSelectionModel();
60 this.colSelectionModel = new DefaultListSelectionModel();
61 }
62 /**
63 * Creates a new tag editor model.
64 *
65 * @param rowSelectionModel the row selection model. Must not be null.
66 * @param colSelectionModel the column selection model. Must not be null.
67 * @throws IllegalArgumentException thrown if {@code rowSelectionModel} is null
68 * @throws IllegalArgumentException thrown if {@code colSelectionModel} is null
69 */
70 public TagEditorModel(DefaultListSelectionModel rowSelectionModel, DefaultListSelectionModel colSelectionModel) throws IllegalArgumentException{
71 CheckParameterUtil.ensureParameterNotNull(rowSelectionModel, "rowSelectionModel");
72 CheckParameterUtil.ensureParameterNotNull(colSelectionModel, "colSelectionModel");
73 this.rowSelectionModel = rowSelectionModel;
74 this.colSelectionModel = colSelectionModel;
75 }
76
77 public void addPropertyChangeListener(PropertyChangeListener listener) {
78 propChangeSupport.addPropertyChangeListener(listener);
79 }
80
81 /**
82 * Replies the row selection model used by this tag editor model
83 *
84 * @return the row selection model used by this tag editor model
85 */
86 public DefaultListSelectionModel getRowSelectionModel() {
87 return rowSelectionModel;
88 }
89
90 /**
91 * Replies the column selection model used by this tag editor model
92 *
93 * @return the column selection model used by this tag editor model
94 */
95 public DefaultListSelectionModel getColumnSelectionModel() {
96 return colSelectionModel;
97 }
98
99 public void removeProperyChangeListener(PropertyChangeListener listener) {
100 propChangeSupport.removePropertyChangeListener(listener);
101 }
102
103 protected void fireDirtyStateChanged(final boolean oldValue, final boolean newValue) {
104 propChangeSupport.firePropertyChange(PROP_DIRTY, oldValue, newValue);
105 }
106
107 protected void setDirty(boolean newValue) {
108 boolean oldValue = dirty;
109 dirty = newValue;
110 if (oldValue != newValue) {
111 fireDirtyStateChanged(oldValue, newValue);
112 }
113 }
114
115 public int getColumnCount() {
116 return 2;
117 }
118
119 public int getRowCount() {
120 return tags.size();
121 }
122
123 public Object getValueAt(int rowIndex, int columnIndex) {
124 if (rowIndex >= getRowCount())
125 throw new IndexOutOfBoundsException("unexpected rowIndex: rowIndex=" + rowIndex);
126
127 TagModel tag = tags.get(rowIndex);
128 switch(columnIndex) {
129 case 0:
130 case 1:
131 return tag;
132
133 default:
134 throw new IndexOutOfBoundsException("unexpected columnIndex: columnIndex=" + columnIndex);
135 }
136 }
137
138 @Override
139 public void setValueAt(Object value, int row, int col) {
140 TagModel tag = get(row);
141 if (tag == null) return;
142 switch(col) {
143 case 0:
144 updateTagName(tag, (String)value);
145 break;
146 case 1:
147 String v = (String)value;
148 if (tag.getValueCount() > 1 && ! v.equals("")) {
149 updateTagValue(tag, v);
150 } else if (tag.getValueCount() <= 1) {
151 updateTagValue(tag, v);
152 }
153 }
154 }
155
156 /**
157 * removes all tags in the model
158 */
159 public void clear() {
160 tags.clear();
161 setDirty(true);
162 fireTableDataChanged();
163 }
164
165 /**
166 * adds a tag to the model
167 *
168 * @param tag the tag. Must not be null.
169 *
170 * @exception IllegalArgumentException thrown, if tag is null
171 */
172 public void add(TagModel tag) {
173 if (tag == null)
174 throw new IllegalArgumentException("argument 'tag' must not be null");
175 tags.add(tag);
176 setDirty(true);
177 fireTableDataChanged();
178 }
179
180 public void prepend(TagModel tag) {
181 if (tag == null)
182 throw new IllegalArgumentException("argument 'tag' must not be null");
183 tags.add(0, tag);
184 setDirty(true);
185 fireTableDataChanged();
186 }
187
188 /**
189 * adds a tag given by a name/value pair to the tag editor model.
190 *
191 * If there is no tag with name <code>name</name> yet, a new {@link TagModel} is created
192 * and append to this model.
193 *
194 * If there is a tag with name <code>name</name>, <code>value</code> is merged to the list
195 * of values for this tag.
196 *
197 * @param name the name; converted to "" if null
198 * @param value the value; converted to "" if null
199 */
200 public void add(String name, String value) {
201 name = (name == null) ? "" : name;
202 value = (value == null) ? "" : value;
203
204 TagModel tag = get(name);
205 if (tag == null) {
206 tag = new TagModel(name, value);
207 int index = tags.size();
208 while (index >= 1 && tags.get(index - 1).getName().isEmpty() && tags.get(index - 1).getValue().isEmpty()) {
209 index--; // If last line(s) is empty, add new tag before it
210 }
211 tags.add(index, tag);
212 } else {
213 tag.addValue(value);
214 }
215 setDirty(true);
216 fireTableDataChanged();
217 }
218
219 /**
220 * replies the tag with name <code>name</code>; null, if no such tag exists
221 * @param name the tag name
222 * @return the tag with name <code>name</code>; null, if no such tag exists
223 */
224 public TagModel get(String name) {
225 name = (name == null) ? "" : name;
226 for (TagModel tag : tags) {
227 if (tag.getName().equals(name))
228 return tag;
229 }
230 return null;
231 }
232
233 public TagModel get(int idx) {
234 if (idx >= tags.size()) return null;
235 TagModel tagModel = tags.get(idx);
236 return tagModel;
237 }
238
239 @Override public boolean isCellEditable(int row, int col) {
240 // all cells are editable
241 return true;
242 }
243
244 /**
245 * deletes the names of the tags given by tagIndices
246 *
247 * @param tagIndices a list of tag indices
248 */
249 public void deleteTagNames(int [] tagIndices) {
250 if (tags == null)
251 return;
252 for (int tagIdx : tagIndices) {
253 TagModel tag = tags.get(tagIdx);
254 if (tag != null) {
255 tag.setName("");
256 }
257 }
258 fireTableDataChanged();
259 setDirty(true);
260 }
261
262 /**
263 * deletes the values of the tags given by tagIndices
264 *
265 * @param tagIndices the lit of tag indices
266 */
267 public void deleteTagValues(int [] tagIndices) {
268 if (tags == null)
269 return;
270 for (int tagIdx : tagIndices) {
271 TagModel tag = tags.get(tagIdx);
272 if (tag != null) {
273 tag.setValue("");
274 }
275 }
276 fireTableDataChanged();
277 setDirty(true);
278 }
279
280 /**
281 * Deletes all tags with name <code>name</code>
282 *
283 * @param name the name. Ignored if null.
284 */
285 public void delete(String name) {
286 if (name == null) return;
287 Iterator<TagModel> it = tags.iterator();
288 boolean changed = false;
289 while(it.hasNext()) {
290 TagModel tm = it.next();
291 if (tm.getName().equals(name)) {
292 changed = true;
293 it.remove();
294 }
295 }
296 if (changed) {
297 fireTableDataChanged();
298 setDirty(true);
299 }
300 }
301 /**
302 * deletes the tags given by tagIndices
303 *
304 * @param tagIndices the list of tag indices
305 */
306 public void deleteTags(int [] tagIndices) {
307 if (tags == null)
308 return;
309 ArrayList<TagModel> toDelete = new ArrayList<TagModel>();
310 for (int tagIdx : tagIndices) {
311 TagModel tag = tags.get(tagIdx);
312 if (tag != null) {
313 toDelete.add(tag);
314 }
315 }
316 for (TagModel tag : toDelete) {
317 tags.remove(tag);
318 }
319 fireTableDataChanged();
320 setDirty(true);
321 }
322
323 /**
324 * creates a new tag and appends it to the model
325 */
326 public void appendNewTag() {
327 TagModel tag = new TagModel();
328 tags.add(tag);
329 fireTableDataChanged();
330 setDirty(true);
331 }
332
333 /**
334 * makes sure the model includes at least one (empty) tag
335 */
336 public void ensureOneTag() {
337 if (tags.size() == 0) {
338 appendNewTag();
339 }
340 }
341
342 /**
343 * initializes the model with the tags of an OSM primitive
344 *
345 * @param primitive the OSM primitive
346 */
347 public void initFromPrimitive(Tagged primitive) {
348 this.tags.clear();
349 for (String key : primitive.keySet()) {
350 String value = primitive.get(key);
351 this.tags.add(new TagModel(key,value));
352 }
353 TagModel tag = new TagModel();
354 sort();
355 tags.add(tag);
356 setDirty(false);
357 fireTableDataChanged();
358 }
359
360 /**
361 * initializes the model with the tags of an OSM primitive
362 *
363 * @param primitive the OSM primitive
364 */
365 public void initFromTags(Map<String,String> tags) {
366 this.tags.clear();
367 for (String key : tags.keySet()) {
368 String value = tags.get(key);
369 this.tags.add(new TagModel(key,value));
370 }
371 sort();
372 TagModel tag = new TagModel();
373 this.tags.add(tag);
374 setDirty(false);
375 }
376
377 /**
378 * Initializes the model with the tags in a tag collection. Removes
379 * all tags if {@code tags} is null.
380 *
381 * @param tags the tags
382 */
383 public void initFromTags(TagCollection tags) {
384 this.tags.clear();
385 if (tags == null){
386 setDirty(false);
387 return;
388 }
389 for (String key : tags.getKeys()) {
390 String value = tags.getJoinedValues(key);
391 this.tags.add(new TagModel(key,value));
392 }
393 sort();
394 // add an empty row
395 TagModel tag = new TagModel();
396 this.tags.add(tag);
397 setDirty(false);
398 }
399
400 /**
401 * applies the current state of the tag editor model to a primitive
402 *
403 * @param primitive the primitive
404 *
405 */
406 public void applyToPrimitive(Tagged primitive) {
407 Map<String,String> tags = primitive.getKeys();
408 applyToTags(tags);
409 primitive.setKeys(tags);
410 }
411
412 /**
413 * applies the current state of the tag editor model to a map of tags
414 *
415 * @param tags the map of key/value pairs
416 *
417 */
418 public void applyToTags(Map<String, String> tags) {
419 tags.clear();
420 for (TagModel tag: this.tags) {
421 // tag still holds an unchanged list of different values for the same key.
422 // no property change command required
423 if (tag.getValueCount() > 1) {
424 continue;
425 }
426
427 // tag name holds an empty key. Don't apply it to the selection.
428 //
429 if (tag.getName().trim().equals("") || tag.getValue().trim().equals("")) {
430 continue;
431 }
432 tags.put(tag.getName().trim(), tag.getValue().trim());
433 }
434 }
435
436 public Map<String,String> getTags() {
437 Map<String,String> tags = new HashMap<String, String>();
438 applyToTags(tags);
439 return tags;
440 }
441
442 /**
443 * Replies the tags in this tag editor model as {@link TagCollection}.
444 *
445 * @return the tags in this tag editor model as {@link TagCollection}
446 */
447 public TagCollection getTagCollection() {
448 return TagCollection.from(getTags());
449 }
450
451 /**
452 * checks whether the tag model includes a tag with a given key
453 *
454 * @param key the key
455 * @return true, if the tag model includes the tag; false, otherwise
456 */
457 public boolean includesTag(String key) {
458 if (key == null) return false;
459 for (TagModel tag : tags) {
460 if (tag.getName().equals(key))
461 return true;
462 }
463 return false;
464 }
465
466 protected Command createUpdateTagCommand(Collection<OsmPrimitive> primitives, TagModel tag) {
467
468 // tag still holds an unchanged list of different values for the same key.
469 // no property change command required
470 if (tag.getValueCount() > 1)
471 return null;
472
473 // tag name holds an empty key. Don't apply it to the selection.
474 //
475 if (tag.getName().trim().equals(""))
476 return null;
477
478 String newkey = tag.getName();
479 String newvalue = tag.getValue();
480
481 ChangePropertyCommand command = new ChangePropertyCommand(primitives,newkey, newvalue);
482 return command;
483 }
484
485 protected Command createDeleteTagsCommand(Collection<OsmPrimitive> primitives) {
486
487 List<String> currentkeys = getKeys();
488 ArrayList<Command> commands = new ArrayList<Command>();
489
490 for (OsmPrimitive primitive : primitives) {
491 for (String oldkey : primitive.keySet()) {
492 if (!currentkeys.contains(oldkey)) {
493 ChangePropertyCommand deleteCommand =
494 new ChangePropertyCommand(primitive,oldkey,null);
495 commands.add(deleteCommand);
496 }
497 }
498 }
499
500 SequenceCommand command = new SequenceCommand(
501 trn("Remove old keys from up to {0} object", "Remove old keys from up to {0} objects", primitives.size(), primitives.size()),
502 commands
503 );
504
505 return command;
506 }
507
508 /**
509 * replies the list of keys of the tags managed by this model
510 *
511 * @return the list of keys managed by this model
512 */
513 public List<String> getKeys() {
514 ArrayList<String> keys = new ArrayList<String>();
515 for (TagModel tag: tags) {
516 if (!tag.getName().trim().equals("")) {
517 keys.add(tag.getName());
518 }
519 }
520 return keys;
521 }
522
523 /**
524 * sorts the current tags according alphabetical order of names
525 */
526 protected void sort() {
527 java.util.Collections.sort(
528 tags,
529 new Comparator<TagModel>() {
530 public int compare(TagModel self, TagModel other) {
531 return self.getName().compareTo(other.getName());
532 }
533 }
534 );
535 }
536
537 /**
538 * updates the name of a tag and sets the dirty state to true if
539 * the new name is different from the old name.
540 *
541 * @param tag the tag
542 * @param newName the new name
543 */
544 public void updateTagName(TagModel tag, String newName) {
545 String oldName = tag.getName();
546 tag.setName(newName);
547 if (! newName.equals(oldName)) {
548 setDirty(true);
549 }
550 SelectionStateMemento memento = new SelectionStateMemento();
551 fireTableDataChanged();
552 memento.apply();
553 }
554
555 /**
556 * updates the value value of a tag and sets the dirty state to true if the
557 * new name is different from the old name
558 *
559 * @param tag the tag
560 * @param newValue the new value
561 */
562 public void updateTagValue(TagModel tag, String newValue) {
563 String oldValue = tag.getValue();
564 tag.setValue(newValue);
565 if (! newValue.equals(oldValue)) {
566 setDirty(true);
567 }
568 SelectionStateMemento memento = new SelectionStateMemento();
569 fireTableDataChanged();
570 memento.apply();
571 }
572
573 /**
574 * replies true, if this model has been updated
575 *
576 * @return true, if this model has been updated
577 */
578 public boolean isDirty() {
579 return dirty;
580 }
581
582 class SelectionStateMemento {
583 private int rowMin;
584 private int rowMax;
585 private int colMin;
586 private int colMax;
587
588 public SelectionStateMemento() {
589 rowMin = rowSelectionModel.getMinSelectionIndex();
590 rowMax = rowSelectionModel.getMaxSelectionIndex();
591 colMin = colSelectionModel.getMinSelectionIndex();
592 colMax = colSelectionModel.getMaxSelectionIndex();
593 }
594
595 public void apply() {
596 rowSelectionModel.setValueIsAdjusting(true);
597 colSelectionModel.setValueIsAdjusting(true);
598 if (rowMin >= 0 && rowMax >=0) {
599 rowSelectionModel.setSelectionInterval(rowMin, rowMax);
600 }
601 if (colMin >=0 && colMax >= 0) {
602 colSelectionModel.setSelectionInterval(colMin, colMax);
603 }
604 rowSelectionModel.setValueIsAdjusting(false);
605 colSelectionModel.setValueIsAdjusting(false);
606 }
607 }
608}
Note: See TracBrowser for help on using the repository browser.