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

Last change on this file since 1409 was 1409, checked in by stoecker, 15 years ago

close #1902. Patch by xeen

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