source: josm/trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java@ 2025

Last change on this file since 2025 was 2025, checked in by Gubaer, 15 years ago

new: improved dialog for uploading/saving modified layers on exit
new: improved dialog for uploading/saving modified layers if layers are deleted
new: new progress monitor which can delegate rendering to any Swing component
more setters/getters for properties in OSM data classes (fields are @deprecated); started to update references in the code base

  • Property svn:eol-style set to native
File size: 17.9 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Date;
11import java.util.HashMap;
12import java.util.Locale;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.data.osm.visitor.Visitor;
18import org.openstreetmap.josm.gui.mappaint.ElemStyle;
19
20
21/**
22 * An OSM primitive can be associated with a key/value pair. It can be created, deleted
23 * and updated within the OSM-Server.
24 *
25 * Although OsmPrimitive is designed as a base class, it is not to be meant to subclass
26 * it by any other than from the package {@link org.openstreetmap.josm.data.osm}. The available primitives are a fixed set that are given
27 * by the server environment and not an extendible data stuff.
28 *
29 * @author imi
30 */
31abstract public class OsmPrimitive implements Comparable<OsmPrimitive> {
32
33 /* mappaint data */
34 public ElemStyle mappaintStyle = null;
35 public Integer mappaintVisibleCode = 0;
36 public Integer mappaintDrawnCode = 0;
37 public Collection<String> errors;
38
39 public void putError(String text, Boolean isError)
40 {
41 if(errors == null) {
42 errors = new ArrayList<String>();
43 }
44 String s = isError ? tr("Error: {0}", text) : tr("Warning: {0}", text);
45 errors.add(s);
46 }
47 public void clearErrors()
48 {
49 errors = null;
50 }
51 /* This should not be called from outside. Fixing the UI to add relevant
52 get/set functions calling this implicitely is preferred, so we can have
53 transparent cache handling in the future. */
54 protected void clearCached()
55 {
56 mappaintVisibleCode = 0;
57 mappaintDrawnCode = 0;
58 mappaintStyle = null;
59 }
60 /* end of mappaint data */
61
62 /**
63 * Unique identifier in OSM. This is used to identify objects on the server.
64 * An id of 0 means an unknown id. The object has not been uploaded yet to
65 * know what id it will get.
66 *
67 * Do not write to this attribute except you know exactly what you are doing.
68 * More specific, it is not good to set this to 0 and think the object is now
69 * new to the server! To create a new object, call the default constructor of
70 * the respective class.
71 *
72 * @deprecated use {@see #getId()}. Don't assign an id, create a primitive with
73 * the respective constructors.
74 */
75 @Deprecated
76 public long id = 0;
77
78 /**
79 * <code>true</code> if the object has been modified since it was loaded from
80 * the server. In this case, on next upload, this object will be updated.
81 * Deleted objects are deleted from the server. If the objects are added (id=0),
82 * the modified is ignored and the object is added to the server.
83 *
84 * @deprecated Please use {@see #setModified()} and {@see #getModified()}
85 */
86 @Deprecated
87 public boolean modified = false;
88
89 /**
90 * <code>true</code>, if the object has been deleted.
91 *
92 * @deprecated use {@see #delete()} and {@see #isDeleted()}
93 */
94 @Deprecated
95 public boolean deleted = false;
96
97 /**
98 * Visibility status as specified by the server. The visible attribute was
99 * introduced with the 0.4 API to be able to communicate deleted objects
100 * (they will have visible=false).
101 *
102 * @deprecated use {@see #isVisible()} and {@see #setVisible(boolean)}
103 */
104 @Deprecated
105 public boolean visible = true;
106
107 /**
108 * User that last modified this primitive, as specified by the server.
109 * Never changed by JOSM.
110 */
111 public User user = null;
112
113 /**
114 * If set to true, this object is currently selected.
115 *
116 * @deprecated use {@see #isSelected()} and {@see #setSelected(boolean)}
117 */
118 @Deprecated
119 public volatile boolean selected = false;
120
121 /**
122 * Sets whether this primitive is selected or not.
123 *
124 * @param selected true, if this primitive is selected; false, otherwise
125 * @since 1899
126 */
127 public void setSelected(boolean selected) {
128 this.selected = selected;
129 }
130 /**
131 * Replies true, if this primitive is selected.
132 *
133 * @return true, if this primitive is selected
134 * @since 1899
135 */
136 public boolean isSelected() {
137 return selected;
138 }
139
140 /**
141 * Marks this primitive as being modified.
142 *
143 * @param modified true, if this primitive is to be modified
144 */
145 public void setModified(boolean modified) {
146 this.modified = modified;
147 }
148
149 /**
150 * Replies <code>true</code> if the object has been modified since it was loaded from
151 * the server. In this case, on next upload, this object will be updated.
152 *
153 * @return <code>true</code> if the object has been modified since it was loaded from
154 * the server
155 */
156 public boolean isModified() {
157 return modified;
158 }
159
160 /**
161 * Replies <code>true</code>, if the object has been deleted.
162 *
163 * @return <code>true</code>, if the object has been deleted.
164 * @see #delete(boolean)
165 */
166 public boolean isDeleted() {
167 return deleted;
168 }
169
170 /**
171 * Replies true if this primitive is either unknown to the server (i.e. its id
172 * is 0) or it is known to the server and it hasn't be deleted on the server.
173 * Replies false, if this primitive is known on the server and has been deleted
174 * on the server.
175 *
176 * @see #setVisible(boolean)
177 */
178 public boolean isVisible() {
179 return visible;
180 }
181
182 /**
183 * Sets whether this primitive is visible, i.e. whether it is known on the server
184 * and not deleted on the server.
185 *
186 * @see #isVisible()
187 * @throws IllegalStateException thrown if visible is set to false on an primitive with
188 * id==0
189 */
190 public void setVisible(boolean visible) throws IllegalStateException{
191 if (id == 0 && visible == false)
192 throw new IllegalStateException(tr("a primitive with id=0 can't be invisible"));
193 this.visible = visible;
194 }
195
196 /**
197 * Replies the id of this primitive.
198 *
199 * @return the id of this primitive.
200 */
201 public long getId() {
202 return id;
203 }
204
205 /**
206 * If set to true, this object is highlighted. Currently this is only used to
207 * show which ways/nodes will connect
208 */
209 public volatile boolean highlighted = false;
210
211 private int timestamp;
212
213 public void setTimestamp(Date timestamp) {
214 this.timestamp = (int)(timestamp.getTime() / 1000);
215 }
216
217 /**
218 * Time of last modification to this object. This is not set by JOSM but
219 * read from the server and delivered back to the server unmodified. It is
220 * used to check against edit conflicts.
221 *
222 */
223 public Date getTimestamp() {
224 return new Date(timestamp * 1000l);
225 }
226
227 public boolean isTimestampEmpty() {
228 return timestamp == 0;
229 }
230
231 /**
232 * If set to true, this object is incomplete, which means only the id
233 * and type is known (type is the objects instance class)
234 */
235 public boolean incomplete = false;
236
237 /**
238 * Contains the version number as returned by the API. Needed to
239 * ensure update consistency
240 */
241 public int version = -1;
242
243 private static Collection<String> uninteresting = null;
244 /**
245 * Contains a list of "uninteresting" keys that do not make an object
246 * "tagged".
247 * Initialized by checkTagged()
248 */
249 public static Collection<String> getUninterestingKeys() {
250 if (uninteresting == null) {
251 uninteresting = Main.pref.getCollection("tags.uninteresting",
252 Arrays.asList(new String[]{"source","note","comment","converted_by","created_by"}));
253 }
254 return uninteresting;
255 }
256
257
258 private static Collection<String> directionKeys = null;
259
260 /**
261 * Contains a list of direction-dependent keys that make an object
262 * direction dependent.
263 * Initialized by checkDirectionTagged()
264 */
265 public static Collection<String> getDirectionKeys() {
266 if(directionKeys == null) {
267 directionKeys = Main.pref.getCollection("tags.direction",
268 Arrays.asList(new String[]{"oneway","incline","incline_steep","aerialway"}));
269 }
270 return directionKeys;
271 }
272
273 /**
274 * Implementation of the visitor scheme. Subclasses have to call the correct
275 * visitor function.
276 * @param visitor The visitor from which the visit() function must be called.
277 */
278 abstract public void visit(Visitor visitor);
279
280 public final void delete(boolean deleted) {
281 this.deleted = deleted;
282 setSelected(false);
283 modified = true;
284 }
285
286 /**
287 * Equal, if the id (and class) is equal.
288 *
289 * An primitive is equal to its incomplete counter part.
290 */
291 @Override public boolean equals(Object obj) {
292 if (id == 0) return obj == this;
293 if (obj instanceof OsmPrimitive)
294 return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
295 return false;
296 }
297
298 /**
299 * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
300 *
301 * An primitive has the same hashcode as its incomplete counterpart.
302 */
303 @Override public final int hashCode() {
304 if (id == 0)
305 return super.hashCode();
306 final int[] ret = new int[1];
307 Visitor v = new Visitor(){
308 public void visit(Node n) { ret[0] = 0; }
309 public void visit(Way w) { ret[0] = 1; }
310 public void visit(Relation e) { ret[0] = 2; }
311 public void visit(Changeset cs) { ret[0] = 3; }
312 };
313 visit(v);
314 return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
315 }
316
317 /*------------
318 * Keys handling
319 ------------*/
320
321 /**
322 * The key/value list for this primitive.
323 *
324 */
325 private Map<String, String> keys;
326
327 /**
328 * Replies the map of key/value pairs. Never replies null. The map can be empty, though.
329 *
330 * @return Keys of this primitive. Changes made in returned map are not mapped
331 * back to the primitive, use setKeys() to modify the keys
332 * @since 1924
333 */
334 public Map<String, String> getKeys() {
335 // TODO More effective map
336 // fix for #3218
337 if (keys == null)
338 return new HashMap<String, String>();
339 else
340 return new HashMap<String, String>(keys);
341 }
342
343 /**
344 * Sets the keys of this primitives to the key/value pairs in <code>keys</code>.
345 * If <code>keys</code> is null removes all existing key/value pairs.
346 *
347 * @param keys the key/value pairs to set. If null, removes all existing key/value pairs.
348 * @since 1924
349 */
350 public void setKeys(Map<String, String> keys) {
351 if (keys == null) {
352 this.keys = null;
353 } else {
354 this.keys = new HashMap<String, String>(keys);
355 }
356 }
357
358 /**
359 * Set the given value to the given key. If key is null, does nothing. If value is null,
360 * removes the key and behaves like {@see #remove(String)}.
361 *
362 * @param key The key, for which the value is to be set. Can be null, does nothing in this case.
363 * @param value The value for the key. If null, removes the respective key/value pair.
364 *
365 * @see #remove(String)
366 */
367 public final void put(String key, String value) {
368 if (key == null)
369 return;
370 else if (value == null) {
371 remove(key);
372 } else {
373 if (keys == null) {
374 keys = new HashMap<String, String>();
375 }
376 keys.put(key, value);
377 }
378 mappaintStyle = null;
379 }
380 /**
381 * Remove the given key from the list
382 *
383 * @param key the key to be removed. Ignored, if key is null.
384 */
385 public final void remove(String key) {
386 if (keys != null) {
387 keys.remove(key);
388 if (keys.isEmpty()) {
389 keys = null;
390 }
391 }
392 mappaintStyle = null;
393 }
394
395 /**
396 * Removes all keys from this primitive.
397 *
398 * @since 1843
399 */
400 public final void removeAll() {
401 keys = null;
402 mappaintStyle = null;
403 }
404
405 /**
406 * Replies the value for key <code>key</code>. Replies null, if <code>key</code> is null.
407 * Replies null, if there is no value for the given key.
408 *
409 * @param key the key. Can be null, replies null in this case.
410 * @return the value for key <code>key</code>.
411 */
412 public final String get(String key) {
413 if (key == null) return null;
414 return keys == null ? null : keys.get(key);
415 }
416
417 public final Collection<Entry<String, String>> entrySet() {
418 if (keys == null)
419 return Collections.emptyList();
420 return keys.entrySet();
421 }
422
423 public final Collection<String> keySet() {
424 if (keys == null)
425 return Collections.emptyList();
426 return keys.keySet();
427 }
428
429 /**
430 * Replies true, if the map of key/value pairs of this primitive is not empty.
431 *
432 * @return true, if the map of key/value pairs of this primitive is not empty; false
433 * otherwise
434 *
435 * @since 1843
436 */
437 public final boolean hasKeys() {
438 return keys != null && !keys.isEmpty();
439 }
440
441 /**
442 * Get and write all attributes from the parameter. Does not fire any listener, so
443 * use this only in the data initializing phase
444 */
445 public void cloneFrom(OsmPrimitive osm) {
446 keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
447 id = osm.id;
448 modified = osm.modified;
449 deleted = osm.deleted;
450 setSelected(osm.isSelected());
451 timestamp = osm.timestamp;
452 version = osm.version;
453 incomplete = osm.incomplete;
454 visible = osm.visible;
455 clearCached();
456 clearErrors();
457 }
458
459 /**
460 * Replies true if this primitive and other are equal with respect to their
461 * semantic attributes.
462 * <ol>
463 * <li>equal id</ol>
464 * <li>both are complete or both are incomplete</li>
465 * <li>both have the same tags</li>
466 * </ol>
467 * @param other
468 * @return true if this primitive and other are equal with respect to their
469 * semantic attributes.
470 */
471 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
472 if (id != other.id)
473 return false;
474 if (incomplete && ! other.incomplete || !incomplete && other.incomplete)
475 return false;
476 return (keys == null ? other.keys==null : keys.equals(other.keys));
477 }
478
479 /**
480 * Replies true if this primitive and other are equal with respect to their
481 * technical attributes. The attributes:
482 * <ol>
483 * <li>deleted</ol>
484 * <li>modified</ol>
485 * <li>timestamp</ol>
486 * <li>version</ol>
487 * <li>visible</ol>
488 * <li>user</ol>
489 * </ol>
490 * have to be equal
491 * @param other the other primitive
492 * @return true if this primitive and other are equal with respect to their
493 * technical attributes
494 */
495 public boolean hasEqualTechnicalAttributes(OsmPrimitive other) {
496 if (other == null) return false;
497
498 return
499 deleted == other.deleted
500 && modified == other.modified
501 && timestamp == other.timestamp
502 && version == other.version
503 && visible == other.visible
504 && (user == null ? other.user==null : user==other.user);
505 }
506
507 /**
508 * true if this object is considered "tagged". To be "tagged", an object
509 * must have one or more "interesting" tags. "created_by" and "source"
510 * are typically considered "uninteresting" and do not make an object
511 * "tagged".
512 */
513 public boolean isTagged() {
514 // TODO Cache value after keys are made private
515 getUninterestingKeys();
516 if (keys != null) {
517 for (Entry<String,String> e : keys.entrySet()) {
518 if (!uninteresting.contains(e.getKey()))
519 return true;
520 }
521 }
522 return false;
523 }
524 /**
525 * true if this object has direction dependent tags (e.g. oneway)
526 */
527 public boolean hasDirectionKeys() {
528 // TODO Cache value after keys are made private
529 getDirectionKeys();
530 if (keys != null) {
531 for (Entry<String,String> e : keys.entrySet()) {
532 if (directionKeys.contains(e.getKey()))
533 return true;
534 }
535 }
536 return false;
537 }
538
539
540 /**
541 * Replies the name of this primitive. The default implementation replies the value
542 * of the tag <tt>name</tt> or null, if this tag is not present.
543 *
544 * @return the name of this primitive
545 */
546 public String getName() {
547 if (get("name") != null)
548 return get("name");
549 return null;
550 }
551
552 /**
553 * Replies the a localized name for this primitive given by the value of the tags (in this order)
554 * <ul>
555 * <li>name:lang_COUNTRY_Variant of the current locale</li>
556 * <li>name:lang_COUNTRY of the current locale</li>
557 * <li>name:lang of the current locale</li>
558 * <li>name of the current locale</li>
559 * </ul>
560 *
561 * null, if no such tag exists
562 *
563 * @return the name of this primitive
564 */
565 public String getLocalName() {
566 String key = "name:" + Locale.getDefault().toString();
567 if (get(key) != null)
568 return get(key);
569 key = "name:" + Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry();
570 if (get(key) != null)
571 return get(key);
572 key = "name:" + Locale.getDefault().getLanguage();
573 if (get(key) != null)
574 return get(key);
575 return getName();
576 }
577
578 /**
579 * Replies the display name of a primitive formatted by <code>formatter</code>
580 *
581 * @return the display name
582 */
583 public abstract String getDisplayName(NameFormatter formatter);
584
585}
586
587
Note: See TracBrowser for help on using the repository browser.