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

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

add error handling for multipolygon drawing

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