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

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

removed usage of tab stops

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