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

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

typo

  • Property svn:eol-style set to native
File size: 11.3 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 * Time of last modification to this object. This is not set by JOSM but
128 * read from the server and delivered back to the server unmodified. It is
129 * used to check against edit conflicts.
130 */
131 public String timestamp = null;
132
133 /**
134 * The timestamp is only parsed when this is really necessary, and this
135 * is the cache for the result.
136 */
137 public Date parsedTimestamp = null;
138
139 /**
140 * If set to true, this object is incomplete, which means only the id
141 * and type is known (type is the objects instance class)
142 */
143 public boolean incomplete = false;
144
145 /**
146 * Contains the version number as returned by the API. Needed to
147 * ensure update consistency
148 */
149 public int version = -1;
150
151 /**
152 * Contains a list of "uninteresting" keys that do not make an object
153 * "tagged".
154 * Initialized by checkTagged()
155 */
156 private static Collection<String> uninteresting = null;
157
158 /**
159 * Contains a list of direction-dependent keys that make an object
160 * direction dependent.
161 * Initialized by checkDirectionTagged()
162 */
163 private static Collection<String> directionKeys = null;
164
165 /**
166 * Implementation of the visitor scheme. Subclasses have to call the correct
167 * visitor function.
168 * @param visitor The visitor from which the visit() function must be called.
169 */
170 abstract public void visit(Visitor visitor);
171
172 public final void delete(boolean deleted) {
173 this.deleted = deleted;
174 selected = false;
175 modified = true;
176 }
177
178 /**
179 * Returns the timestamp for this object, or the current time if none is set.
180 * Internally, parses the timestamp from XML into a Date object and caches it
181 * for possible repeated calls.
182 */
183 public Date getTimestamp() {
184 if (parsedTimestamp == null) {
185 try {
186 parsedTimestamp = DateParser.parse(timestamp);
187 } catch (ParseException ex) {
188 parsedTimestamp = new Date();
189 }
190 }
191 return parsedTimestamp;
192 }
193
194 /**
195 * Equal, if the id (and class) is equal.
196 *
197 * An primitive is equal to its incomplete counter part.
198 */
199 @Override public boolean equals(Object obj) {
200 if (id == 0) return obj == this;
201 if (obj instanceof OsmPrimitive) { // not null too
202 return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
203 }
204 return false;
205 }
206
207 /**
208 * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
209 *
210 * An primitive has the same hashcode as its incomplete counterpart.
211 */
212 @Override public final int hashCode() {
213 if (id == 0)
214 return super.hashCode();
215 final int[] ret = new int[1];
216 Visitor v = new Visitor(){
217 public void visit(Node n) { ret[0] = 1; }
218 public void visit(Way w) { ret[0] = 2; }
219 public void visit(Relation e) { ret[0] = 3; }
220 };
221 visit(v);
222 return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
223 }
224
225 /**
226 * Set the given value to the given key
227 * @param key The key, for which the value is to be set.
228 * @param value The value for the key.
229 */
230 public final void put(String key, String value) {
231 if (value == null)
232 remove(key);
233 else {
234 if (keys == null)
235 keys = new HashMap<String, String>();
236 keys.put(key, value);
237 }
238 checkTagged();
239 checkDirectionTagged();
240 mappaintStyle = null;
241 }
242 /**
243 * Remove the given key from the list.
244 */
245 public final void remove(String key) {
246 if (keys != null) {
247 keys.remove(key);
248 if (keys.isEmpty())
249 keys = null;
250 }
251 checkTagged();
252 checkDirectionTagged();
253 mappaintStyle = null;
254 }
255
256 public String getName() {
257 return null;
258 }
259
260 public final String get(String key) {
261 return keys == null ? null : keys.get(key);
262 }
263
264 public final Collection<Entry<String, String>> entrySet() {
265 if (keys == null)
266 return Collections.emptyList();
267 return keys.entrySet();
268 }
269
270 public final Collection<String> keySet() {
271 if (keys == null)
272 return Collections.emptyList();
273 return keys.keySet();
274 }
275
276 /**
277 * Get and write all attributes from the parameter. Does not fire any listener, so
278 * use this only in the data initializing phase
279 */
280 public void cloneFrom(OsmPrimitive osm) {
281 keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
282 id = osm.id;
283 modified = osm.modified;
284 deleted = osm.deleted;
285 selected = osm.selected;
286 timestamp = osm.timestamp;
287 version = osm.version;
288 tagged = osm.tagged;
289 incomplete = osm.incomplete;
290 clearCached();
291 clearErrors();
292 }
293
294 /**
295 * Perform an equality compare for all non-volatile fields not only for the id
296 * but for the whole object (for conflict resolving)
297 * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared
298 */
299 public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
300 return
301 id == osm.id &&
302 incomplete == osm.incomplete &&
303 (semanticOnly || (modified == osm.modified)) &&
304 deleted == osm.deleted &&
305 (semanticOnly || (timestamp == null ? osm.timestamp==null : timestamp.equals(osm.timestamp))) &&
306 (semanticOnly || (version==osm.version)) &&
307 (semanticOnly || (user == null ? osm.user==null : user==osm.user)) &&
308 (semanticOnly || (visible == osm.visible)) &&
309 (keys == null ? osm.keys==null : keys.equals(osm.keys));
310 }
311
312 public String getTimeStr() {
313 return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);
314 }
315
316 /**
317 * Updates the "tagged" flag. "keys" property should probably be made private
318 * to make sure this gets called when keys are set.
319 */
320 public void checkTagged() {
321 tagged = false;
322 if(uninteresting == null)
323 uninteresting = new HashSet<String>(Arrays.asList(Main.pref.get("tags.uninteresting",
324 "source;note;converted_by;created_by").split(";")));
325 if (keys != null) {
326 for (Entry<String,String> e : keys.entrySet()) {
327 if (!uninteresting.contains(e.getKey())) {
328 tagged = true;
329 break;
330 }
331 }
332 }
333 }
334 /**
335 * Updates the "hasDirectionKeys" flag. "keys" property should probably be made private
336 * to make sure this gets called when keys are set.
337 */
338 public void checkDirectionTagged() {
339 hasDirectionKeys = false;
340 if(directionKeys == null)
341 directionKeys = new HashSet<String>(Arrays.asList(Main.pref.get("tags.direction",
342 "oneway;incline;incline_steep;aerialway").split(";")));
343 if (keys != null) {
344 for (Entry<String,String> e : keys.entrySet()) {
345 if (directionKeys.contains(e.getKey())) {
346 hasDirectionKeys = true;
347 break;
348 }
349 }
350 }
351 }
352}
Note: See TracBrowser for help on using the repository browser.