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

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

new: MultiFetchServerObjectReader using APIs Multi Fetch method
update: now uses Multi Fetch to check for deleted primitives on the server
update: now uses Multi Fetch to update the selected primitives with the state from the server
fixed: cleaned up merging in MergeVisitor
new: conflict resolution dialog; now resolves conflicts due to different visibilities
new: replacement for realEqual() on OsmPrimitive and derived classes; realEqual now @deprecated
fixed: cleaning up OsmReader
fixed: progress indication in OsmApi

  • Property svn:eol-style set to native
File size: 12.2 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.Map;
13import java.util.Map.Entry;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.osm.visitor.Visitor;
17import org.openstreetmap.josm.gui.mappaint.ElemStyle;
18
19
20/**
21 * An OSM primitive can be associated with a key/value pair. It can be created, deleted
22 * and updated within the OSM-Server.
23 *
24 * Although OsmPrimitive is designed as a base class, it is not to be meant to subclass
25 * it by any other than from the package {@link org.openstreetmap.josm.data.osm}. The available primitives are a fixed set that are given
26 * by the server environment and not an extendible data stuff.
27 *
28 * @author imi
29 */
30abstract public class OsmPrimitive implements Comparable<OsmPrimitive> {
31
32 /**
33 * The key/value list for this primitive.
34 */
35 public Map<String, String> keys;
36
37 /* mappaint data */
38 public ElemStyle mappaintStyle = null;
39 public Integer mappaintVisibleCode = 0;
40 public Integer mappaintDrawnCode = 0;
41 public Collection<String> errors;
42
43 public void putError(String text, Boolean isError)
44 {
45 if(errors == null) {
46 errors = new ArrayList<String>();
47 }
48 String s = isError ? tr("Error: {0}", text) : tr("Warning: {0}", text);
49 errors.add(s);
50 }
51 public void clearErrors()
52 {
53 errors = null;
54 }
55 /* This should not be called from outside. Fixing the UI to add relevant
56 get/set functions calling this implicitely is preferred, so we can have
57 transparent cache handling in the future. */
58 protected void clearCached()
59 {
60 mappaintVisibleCode = 0;
61 mappaintDrawnCode = 0;
62 mappaintStyle = null;
63 }
64 /* end of mappaint data */
65
66 /**
67 * Unique identifier in OSM. This is used to identify objects on the server.
68 * An id of 0 means an unknown id. The object has not been uploaded yet to
69 * know what id it will get.
70 *
71 * Do not write to this attribute except you know exactly what you are doing.
72 * More specific, it is not good to set this to 0 and think the object is now
73 * new to the server! To create a new object, call the default constructor of
74 * the respective class.
75 */
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 public boolean modified = false;
85
86 /**
87 * <code>true</code>, if the object has been deleted.
88 */
89 public boolean deleted = false;
90
91 /**
92 * Visibility status as specified by the server. The visible attribute was
93 * introduced with the 0.4 API to be able to communicate deleted objects
94 * (they will have visible=false).
95 */
96 public boolean visible = true;
97
98 /**
99 * User that last modified this primitive, as specified by the server.
100 * Never changed by JOSM.
101 */
102 public User user = null;
103
104 /**
105 * If set to true, this object is currently selected.
106 */
107 public volatile boolean selected = false;
108
109 /**
110 * If set to true, this object is highlighted. Currently this is only used to
111 * show which ways/nodes will connect
112 */
113 public volatile boolean highlighted = false;
114
115 private int timestamp;
116
117 public void setTimestamp(Date timestamp) {
118 this.timestamp = (int)(timestamp.getTime() / 1000);
119 }
120
121 /**
122 * Time of last modification to this object. This is not set by JOSM but
123 * read from the server and delivered back to the server unmodified. It is
124 * used to check against edit conflicts.
125 *
126 */
127 public Date getTimestamp() {
128 return new Date(timestamp * 1000l);
129 }
130
131 public boolean isTimestampEmpty() {
132 return timestamp == 0;
133 }
134
135 /**
136 * If set to true, this object is incomplete, which means only the id
137 * and type is known (type is the objects instance class)
138 */
139 public boolean incomplete = false;
140
141 /**
142 * Contains the version number as returned by the API. Needed to
143 * ensure update consistency
144 */
145 public int version = -1;
146
147 private static Collection<String> uninteresting = null;
148 /**
149 * Contains a list of "uninteresting" keys that do not make an object
150 * "tagged".
151 * Initialized by checkTagged()
152 */
153 public static Collection<String> getUninterestingKeys() {
154 if (uninteresting == null) {
155 uninteresting = Main.pref.getCollection("tags.uninteresting",
156 Arrays.asList(new String[]{"source","note","comment","converted_by","created_by"}));
157 }
158 return uninteresting;
159 }
160
161
162 private static Collection<String> directionKeys = null;
163
164 /**
165 * Contains a list of direction-dependent keys that make an object
166 * direction dependent.
167 * Initialized by checkDirectionTagged()
168 */
169 public static Collection<String> getDirectionKeys() {
170 if(directionKeys == null) {
171 directionKeys = Main.pref.getCollection("tags.direction",
172 Arrays.asList(new String[]{"oneway","incline","incline_steep","aerialway"}));
173 }
174 return directionKeys;
175 }
176
177 /**
178 * Implementation of the visitor scheme. Subclasses have to call the correct
179 * visitor function.
180 * @param visitor The visitor from which the visit() function must be called.
181 */
182 abstract public void visit(Visitor visitor);
183
184 public final void delete(boolean deleted) {
185 this.deleted = deleted;
186 selected = false;
187 modified = true;
188 }
189
190 /**
191 * Equal, if the id (and class) is equal.
192 *
193 * An primitive is equal to its incomplete counter part.
194 */
195 @Override public boolean equals(Object obj) {
196 if (id == 0) return obj == this;
197 if (obj instanceof OsmPrimitive)
198 return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
199 return false;
200 }
201
202 /**
203 * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
204 *
205 * An primitive has the same hashcode as its incomplete counterpart.
206 */
207 @Override public final int hashCode() {
208 if (id == 0)
209 return super.hashCode();
210 final int[] ret = new int[1];
211 Visitor v = new Visitor(){
212 public void visit(Node n) { ret[0] = 0; }
213 public void visit(Way w) { ret[0] = 1; }
214 public void visit(Relation e) { ret[0] = 2; }
215 public void visit(Changeset cs) { ret[0] = 3; }
216 };
217 visit(v);
218 return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
219 }
220
221 /**
222 * Set the given value to the given key
223 * @param key The key, for which the value is to be set.
224 * @param value The value for the key.
225 */
226 public final void put(String key, String value) {
227 if (value == null) {
228 remove(key);
229 } else {
230 if (keys == null) {
231 keys = new HashMap<String, String>();
232 }
233 keys.put(key, value);
234 }
235 mappaintStyle = null;
236 }
237 /**
238 * Remove the given key from the list.
239 */
240 public final void remove(String key) {
241 if (keys != null) {
242 keys.remove(key);
243 if (keys.isEmpty()) {
244 keys = null;
245 }
246 }
247 mappaintStyle = null;
248 }
249
250 public String getName() {
251 return null;
252 }
253
254 public final String get(String key) {
255 return keys == null ? null : keys.get(key);
256 }
257
258 public final Collection<Entry<String, String>> entrySet() {
259 if (keys == null)
260 return Collections.emptyList();
261 return keys.entrySet();
262 }
263
264 public final Collection<String> keySet() {
265 if (keys == null)
266 return Collections.emptyList();
267 return keys.keySet();
268 }
269
270 /**
271 * Get and write all attributes from the parameter. Does not fire any listener, so
272 * use this only in the data initializing phase
273 */
274 public void cloneFrom(OsmPrimitive osm) {
275 keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
276 id = osm.id;
277 modified = osm.modified;
278 deleted = osm.deleted;
279 selected = osm.selected;
280 timestamp = osm.timestamp;
281 version = osm.version;
282 incomplete = osm.incomplete;
283 visible = osm.visible;
284 clearCached();
285 clearErrors();
286 }
287
288 /**
289 * Perform an equality compare for all non-volatile fields not only for the id
290 * but for the whole object (for conflict resolving)
291 * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared
292 *
293 * @deprecated
294 * @see #hasEqualSemanticAttributes(OsmPrimitive)
295 * @see #hasEqualTechnicalAttributes(OsmPrimitive)
296 */
297 @Deprecated
298 public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
299 return id == osm.id
300 && incomplete == osm.incomplete
301 && deleted == osm.deleted
302 && (semanticOnly || (
303 modified == osm.modified
304 && timestamp == osm.timestamp
305 && version == osm.version
306 && visible == osm.visible
307 && (user == null ? osm.user==null : user==osm.user))
308 )
309 && (keys == null ? osm.keys==null : keys.equals(osm.keys));
310 }
311
312 /**
313 * Replies true if this primitive and other are equal with respect to their
314 * semantic attributes.
315 * <ol>
316 * <li>equal id</ol>
317 * <li>both are complete or both are incomplete</li>
318 * <li>both have the same tags</li>
319 * </ol>
320 * @param other
321 * @return true if this primitive and other are equal with respect to their
322 * semantic attributes.
323 */
324 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
325 if (id != other.id)
326 return false;
327 if (incomplete && ! other.incomplete || !incomplete && other.incomplete)
328 return false;
329 return (keys == null ? other.keys==null : keys.equals(other.keys));
330 }
331
332 /**
333 * Replies true if this primitive and other are equal with respect to their
334 * technical attributes. The attributes:
335 * <ol>
336 * <li>deleted</ol>
337 * <li>modified</ol>
338 * <li>timestamp</ol>
339 * <li>version</ol>
340 * <li>visible</ol>
341 * <li>user</ol>
342 * </ol>
343 * have to be equal
344 * @param other the other primitive
345 * @return true if this primitive and other are equal with respect to their
346 * technical attributes
347 */
348 public boolean hasEqualTechnicalAttributes(OsmPrimitive other) {
349 if (other == null) return false;
350
351 return
352 deleted == other.deleted
353 && modified == other.modified
354 && timestamp == other.timestamp
355 && version == other.version
356 && visible == other.visible
357 && (user == null ? other.user==null : user==other.user);
358 }
359
360 /**
361 * true if this object is considered "tagged". To be "tagged", an object
362 * must have one or more "interesting" tags. "created_by" and "source"
363 * are typically considered "uninteresting" and do not make an object
364 * "tagged".
365 */
366 public boolean isTagged() {
367 // TODO Cache value after keys are made private
368 getUninterestingKeys();
369 if (keys != null) {
370 for (Entry<String,String> e : keys.entrySet()) {
371 if (!uninteresting.contains(e.getKey()))
372 return true;
373 }
374 }
375 return false;
376 }
377 /**
378 * true if this object has direction dependent tags (e.g. oneway)
379 */
380 public boolean hasDirectionKeys() {
381 // TODO Cache value after keys are made private
382 getDirectionKeys();
383 if (keys != null) {
384 for (Entry<String,String> e : keys.entrySet()) {
385 if (directionKeys.contains(e.getKey()))
386 return true;
387 }
388 }
389 return false;
390 }
391}
Note: See TracBrowser for help on using the repository browser.