001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.text.MessageFormat;
007import java.util.Arrays;
008import java.util.Collection;
009import java.util.Collections;
010import java.util.Date;
011import java.util.HashMap;
012import java.util.HashSet;
013import java.util.Locale;
014import java.util.Map;
015import java.util.Map.Entry;
016import java.util.Objects;
017import java.util.Set;
018import java.util.concurrent.atomic.AtomicLong;
019
020/**
021* Abstract class to represent common features of the datatypes primitives.
022*
023* @since 4099
024*/
025public abstract class AbstractPrimitive implements IPrimitive {
026
027    private static final AtomicLong idCounter = new AtomicLong(0);
028
029    static long generateUniqueId() {
030        return idCounter.decrementAndGet();
031    }
032
033    /**
034     * This flag shows, that the properties have been changed by the user
035     * and on upload the object will be send to the server.
036     */
037    protected static final int FLAG_MODIFIED = 1 << 0;
038
039    /**
040     * This flag is false, if the object is marked
041     * as deleted on the server.
042     */
043    protected static final int FLAG_VISIBLE  = 1 << 1;
044
045    /**
046     * An object that was deleted by the user.
047     * Deleted objects are usually hidden on the map and a request
048     * for deletion will be send to the server on upload.
049     * An object usually cannot be deleted if it has non-deleted
050     * objects still referring to it.
051     */
052    protected static final int FLAG_DELETED  = 1 << 2;
053
054    /**
055     * A primitive is incomplete if we know its id and type, but nothing more.
056     * Typically some members of a relation are incomplete until they are
057     * fetched from the server.
058     */
059    protected static final int FLAG_INCOMPLETE = 1 << 3;
060
061    /**
062     * Put several boolean flags to one short int field to save memory.
063     * Other bits of this field are used in subclasses.
064     */
065    protected volatile short flags = FLAG_VISIBLE;   // visible per default
066
067    /*-------------------
068     * OTHER PROPERTIES
069     *-------------------*/
070
071    /**
072     * Unique identifier in OSM. This is used to identify objects on the server.
073     * An id of 0 means an unknown id. The object has not been uploaded yet to
074     * know what id it will get.
075     */
076    protected long id = 0;
077
078    /**
079     * User that last modified this primitive, as specified by the server.
080     * Never changed by JOSM.
081     */
082    protected User user = null;
083
084    /**
085     * Contains the version number as returned by the API. Needed to
086     * ensure update consistency
087     */
088    protected int version = 0;
089
090    /**
091     * The id of the changeset this primitive was last uploaded to.
092     * 0 if it wasn't uploaded to a changeset yet of if the changeset
093     * id isn't known.
094     */
095    protected int changesetId;
096
097    protected int timestamp;
098
099    /**
100     * Get and write all attributes from the parameter. Does not fire any listener, so
101     * use this only in the data initializing phase
102     * @param other the primitive to clone data from
103     */
104    public void cloneFrom(AbstractPrimitive other) {
105        setKeys(other.getKeys());
106        id = other.id;
107        if (id <=0) {
108            // reset version and changeset id
109            version = 0;
110            changesetId = 0;
111        }
112        timestamp = other.timestamp;
113        if (id > 0) {
114            version = other.version;
115        }
116        flags = other.flags;
117        user= other.user;
118        if (id > 0 && other.changesetId > 0) {
119            // #4208: sometimes we cloned from other with id < 0 *and*
120            // an assigned changeset id. Don't know why yet. For primitives
121            // with id < 0 we don't propagate the changeset id any more.
122            //
123            setChangesetId(other.changesetId);
124        }
125    }
126
127    /**
128     * Replies the version number as returned by the API. The version is 0 if the id is 0 or
129     * if this primitive is incomplete.
130     *
131     * @see PrimitiveData#setVersion(int)
132     */
133    @Override
134    public int getVersion() {
135        return version;
136    }
137
138    /**
139     * Replies the id of this primitive.
140     *
141     * @return the id of this primitive.
142     */
143    @Override
144    public long getId() {
145        long id = this.id;
146        return id >= 0?id:0;
147    }
148
149    /**
150     * Gets a unique id representing this object.
151     *
152     * @return Osm id if primitive already exists on the server. Unique negative value if primitive is new
153     */
154    @Override
155    public long getUniqueId() {
156        return id;
157    }
158
159    /**
160     *
161     * @return True if primitive is new (not yet uploaded the server, id &lt;= 0)
162     */
163    @Override
164    public boolean isNew() {
165        return id <= 0;
166    }
167
168    /**
169     *
170     * @return True if primitive is new or undeleted
171     * @see #isNew()
172     * @see #isUndeleted()
173     */
174    @Override
175    public boolean isNewOrUndeleted() {
176        return (id <= 0) || ((flags & (FLAG_VISIBLE + FLAG_DELETED)) == 0);
177    }
178
179    /**
180     * Sets the id and the version of this primitive if it is known to the OSM API.
181     *
182     * Since we know the id and its version it can't be incomplete anymore. incomplete
183     * is set to false.
184     *
185     * @param id the id. &gt; 0 required
186     * @param version the version &gt; 0 required
187     * @throws IllegalArgumentException thrown if id &lt;= 0
188     * @throws IllegalArgumentException thrown if version &lt;= 0
189     * @throws DataIntegrityProblemException If id is changed and primitive was already added to the dataset
190     */
191    @Override
192    public void setOsmId(long id, int version) {
193        if (id <= 0)
194            throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
195        if (version <= 0)
196            throw new IllegalArgumentException(tr("Version > 0 expected. Got {0}.", version));
197        this.id = id;
198        this.version = version;
199        this.setIncomplete(false);
200    }
201
202    /**
203     * Clears the metadata, including id and version known to the OSM API.
204     * The id is a new unique id. The version, changeset and timestamp are set to 0.
205     * incomplete and deleted are set to false. It's preferred to use copy constructor with clearMetadata set to true instead
206     * of calling this method.
207     * @since 6140
208     */
209    public void clearOsmMetadata() {
210        // Not part of dataset - no lock necessary
211        this.id = generateUniqueId();
212        this.version = 0;
213        this.user = null;
214        this.changesetId = 0; // reset changeset id on a new object
215        this.timestamp = 0;
216        this.setIncomplete(false);
217        this.setDeleted(false);
218        this.setVisible(true);
219    }
220
221    /**
222     * Replies the user who has last touched this object. May be null.
223     *
224     * @return the user who has last touched this object. May be null.
225     */
226    @Override
227    public User getUser() {
228        return user;
229    }
230
231    /**
232     * Sets the user who has last touched this object.
233     *
234     * @param user the user
235     */
236    @Override
237    public void setUser(User user) {
238        this.user = user;
239    }
240
241    /**
242     * Replies the id of the changeset this primitive was last uploaded to.
243     * 0 if this primitive wasn't uploaded to a changeset yet or if the
244     * changeset isn't known.
245     *
246     * @return the id of the changeset this primitive was last uploaded to.
247     */
248    @Override
249    public int getChangesetId() {
250        return changesetId;
251    }
252
253    /**
254     * Sets the changeset id of this primitive. Can't be set on a new
255     * primitive.
256     *
257     * @param changesetId the id. &gt;= 0 required.
258     * @throws IllegalStateException thrown if this primitive is new.
259     * @throws IllegalArgumentException thrown if id &lt; 0
260     */
261    @Override
262    public void setChangesetId(int changesetId) throws IllegalStateException, IllegalArgumentException {
263        if (this.changesetId == changesetId)
264            return;
265        if (changesetId < 0)
266            throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' >= 0 expected, got {1}", "changesetId", changesetId));
267        if (isNew() && changesetId > 0)
268            throw new IllegalStateException(tr("Cannot assign a changesetId > 0 to a new primitive. Value of changesetId is {0}", changesetId));
269
270        this.changesetId = changesetId;
271    }
272
273    /**
274     * Replies the unique primitive id for this primitive
275     *
276     * @return the unique primitive id for this primitive
277     */
278    @Override
279    public PrimitiveId getPrimitiveId() {
280        return new SimplePrimitiveId(getUniqueId(), getType());
281    }
282
283    public OsmPrimitiveType getDisplayType() {
284        return getType();
285    }
286
287    @Override
288    public void setTimestamp(Date timestamp) {
289        this.timestamp = (int)(timestamp.getTime() / 1000);
290    }
291
292    /**
293     * Time of last modification to this object. This is not set by JOSM but
294     * read from the server and delivered back to the server unmodified. It is
295     * used to check against edit conflicts.
296     *
297     * @return date of last modification
298     */
299    @Override
300    public Date getTimestamp() {
301        return new Date(timestamp * 1000L);
302    }
303
304    @Override
305    public boolean isTimestampEmpty() {
306        return timestamp == 0;
307    }
308
309    /* -------
310    /* FLAGS
311    /* ------*/
312
313    protected void updateFlags(int flag, boolean value) {
314        if (value) {
315            flags |= flag;
316        } else {
317            flags &= ~flag;
318        }
319    }
320
321    /**
322     * Marks this primitive as being modified.
323     *
324     * @param modified true, if this primitive is to be modified
325     */
326    @Override
327    public void setModified(boolean modified) {
328        updateFlags(FLAG_MODIFIED, modified);
329    }
330
331    /**
332     * Replies <code>true</code> if the object has been modified since it was loaded from
333     * the server. In this case, on next upload, this object will be updated.
334     *
335     * Deleted objects are deleted from the server. If the objects are added (id=0),
336     * the modified is ignored and the object is added to the server.
337     *
338     * @return <code>true</code> if the object has been modified since it was loaded from
339     * the server
340     */
341    @Override
342    public boolean isModified() {
343        return (flags & FLAG_MODIFIED) != 0;
344    }
345
346    /**
347     * Replies <code>true</code>, if the object has been deleted.
348     *
349     * @return <code>true</code>, if the object has been deleted.
350     * @see #setDeleted(boolean)
351     */
352    @Override
353    public boolean isDeleted() {
354        return (flags & FLAG_DELETED) != 0;
355    }
356
357    /**
358     * Replies <code>true</code> if the object has been deleted on the server and was undeleted by the user.
359     * @return <code>true</code> if the object has been undeleted
360     */
361    public boolean isUndeleted() {
362        return (flags & (FLAG_VISIBLE + FLAG_DELETED)) == 0;
363    }
364
365    /**
366     * Replies <code>true</code>, if the object is usable
367     * (i.e. complete and not deleted).
368     *
369     * @return <code>true</code>, if the object is usable.
370     * @see #setDeleted(boolean)
371     */
372    public boolean isUsable() {
373        return (flags & (FLAG_DELETED + FLAG_INCOMPLETE)) == 0;
374    }
375
376    /**
377     * Checks if object is known to the server.
378     * Replies true if this primitive is either unknown to the server (i.e. its id
379     * is 0) or it is known to the server and it hasn't be deleted on the server.
380     * Replies false, if this primitive is known on the server and has been deleted
381     * on the server.
382     *
383     * @return <code>true</code>, if the object is visible on server.
384     * @see #setVisible(boolean)
385     */
386    @Override
387    public boolean isVisible() {
388        return (flags & FLAG_VISIBLE) != 0;
389    }
390
391    /**
392     * Sets whether this primitive is visible, i.e. whether it is known on the server
393     * and not deleted on the server.
394     *
395     * @see #isVisible()
396     * @throws IllegalStateException thrown if visible is set to false on an primitive with
397     * id==0
398     */
399    @Override
400    public void setVisible(boolean visible) throws IllegalStateException{
401        if (isNew() && !visible)
402            throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
403        updateFlags(FLAG_VISIBLE, visible);
404    }
405
406    /**
407     * Sets whether this primitive is deleted or not.
408     *
409     * Also marks this primitive as modified if deleted is true.
410     *
411     * @param deleted  true, if this primitive is deleted; false, otherwise
412     */
413    @Override
414    public void setDeleted(boolean deleted) {
415        updateFlags(FLAG_DELETED, deleted);
416        setModified(deleted ^ !isVisible());
417    }
418
419    /**
420     * If set to true, this object is incomplete, which means only the id
421     * and type is known (type is the objects instance class)
422     */
423    protected void setIncomplete(boolean incomplete) {
424        updateFlags(FLAG_INCOMPLETE, incomplete);
425    }
426
427    @Override
428    public boolean isIncomplete() {
429        return (flags & FLAG_INCOMPLETE) != 0;
430    }
431
432    protected String getFlagsAsString() {
433        StringBuilder builder = new StringBuilder();
434
435        if (isIncomplete()) {
436            builder.append("I");
437        }
438        if (isModified()) {
439            builder.append("M");
440        }
441        if (isVisible()) {
442            builder.append("V");
443        }
444        if (isDeleted()) {
445            builder.append("D");
446        }
447        return builder.toString();
448    }
449
450    /*------------
451     * Keys handling
452     ------------*/
453
454    // Note that all methods that read keys first make local copy of keys array reference. This is to ensure thread safety - reading
455    // doesn't have to be locked so it's possible that keys array will be modified. But all write methods make copy of keys array so
456    // the array itself will be never modified - only reference will be changed
457
458    /**
459     * The key/value list for this primitive.
460     *
461     */
462    protected String[] keys;
463
464    /**
465     * Replies the map of key/value pairs. Never replies null. The map can be empty, though.
466     *
467     * @return tags of this primitive. Changes made in returned map are not mapped
468     * back to the primitive, use setKeys() to modify the keys
469     */
470    @Override
471    public Map<String, String> getKeys() {
472        Map<String, String> result = new HashMap<>();
473        String[] keys = this.keys;
474        if (keys != null) {
475            for (int i=0; i<keys.length ; i+=2) {
476                result.put(keys[i], keys[i + 1]);
477            }
478        }
479        return result;
480    }
481
482    /**
483     * Sets the keys of this primitives to the key/value pairs in <code>keys</code>.
484     * Old key/value pairs are removed.
485     * If <code>keys</code> is null, clears existing key/value pairs.
486     *
487     * @param keys the key/value pairs to set. If null, removes all existing key/value pairs.
488     */
489    @Override
490    public void setKeys(Map<String, String> keys) {
491        Map<String, String> originalKeys = getKeys();
492        if (keys == null || keys.isEmpty()) {
493            this.keys = null;
494            keysChangedImpl(originalKeys);
495            return;
496        }
497        String[] newKeys = new String[keys.size() * 2];
498        int index = 0;
499        for (Entry<String, String> entry:keys.entrySet()) {
500            newKeys[index++] = entry.getKey();
501            newKeys[index++] = entry.getValue();
502        }
503        this.keys = newKeys;
504        keysChangedImpl(originalKeys);
505    }
506
507    /**
508     * Set the given value to the given key. If key is null, does nothing. If value is null,
509     * removes the key and behaves like {@link #remove(String)}.
510     *
511     * @param key  The key, for which the value is to be set. Can be null, does nothing in this case.
512     * @param value The value for the key. If null, removes the respective key/value pair.
513     *
514     * @see #remove(String)
515     */
516    @Override
517    public void put(String key, String value) {
518        Map<String, String> originalKeys = getKeys();
519        if (key == null)
520            return;
521        else if (value == null) {
522            remove(key);
523        } else if (keys == null){
524            keys = new String[] {key, value};
525            keysChangedImpl(originalKeys);
526        } else {
527            for (int i=0; i<keys.length;i+=2) {
528                if (keys[i].equals(key)) {
529                    keys[i+1] = value;  // This modifies the keys array but it doesn't make it invalidate for any time so its ok (see note no top)
530                    keysChangedImpl(originalKeys);
531                    return;
532                }
533            }
534            String[] newKeys = new String[keys.length + 2];
535            for (int i=0; i< keys.length;i+=2) {
536                newKeys[i] = keys[i];
537                newKeys[i+1] = keys[i+1];
538            }
539            newKeys[keys.length] = key;
540            newKeys[keys.length + 1] = value;
541            keys = newKeys;
542            keysChangedImpl(originalKeys);
543        }
544    }
545
546    /**
547     * Remove the given key from the list
548     *
549     * @param key  the key to be removed. Ignored, if key is null.
550     */
551    @Override
552    public void remove(String key) {
553        if (key == null || keys == null) return;
554        if (!hasKey(key))
555            return;
556        Map<String, String> originalKeys = getKeys();
557        if (keys.length == 2) {
558            keys = null;
559            keysChangedImpl(originalKeys);
560            return;
561        }
562        String[] newKeys = new String[keys.length - 2];
563        int j=0;
564        for (int i=0; i < keys.length; i+=2) {
565            if (!keys[i].equals(key)) {
566                newKeys[j++] = keys[i];
567                newKeys[j++] = keys[i+1];
568            }
569        }
570        keys = newKeys;
571        keysChangedImpl(originalKeys);
572    }
573
574    /**
575     * Removes all keys from this primitive.
576     */
577    @Override
578    public void removeAll() {
579        if (keys != null) {
580            Map<String, String> originalKeys = getKeys();
581            keys = null;
582            keysChangedImpl(originalKeys);
583        }
584    }
585
586    /**
587     * Replies the value for key <code>key</code>. Replies null, if <code>key</code> is null.
588     * Replies null, if there is no value for the given key.
589     *
590     * @param key the key. Can be null, replies null in this case.
591     * @return the value for key <code>key</code>.
592     */
593    @Override
594    public final String get(String key) {
595        String[] keys = this.keys;
596        if (key == null)
597            return null;
598        if (keys == null)
599            return null;
600        for (int i=0; i<keys.length;i+=2) {
601            if (keys[i].equals(key)) return keys[i+1];
602        }
603        return null;
604    }
605
606    /**
607     * Returns true if the {@code key} corresponds to an OSM true value.
608     * @see OsmUtils#isTrue(String)
609     */
610    public final boolean isKeyTrue(String key) {
611        return OsmUtils.isTrue(get(key));
612    }
613
614    /**
615     * Returns true if the {@code key} corresponds to an OSM false value.
616     * @see OsmUtils#isFalse(String)
617     */
618    public final boolean isKeyFalse(String key) {
619        return OsmUtils.isFalse(get(key));
620    }
621
622    public final String getIgnoreCase(String key) {
623        String[] keys = this.keys;
624        if (key == null)
625            return null;
626        if (keys == null)
627            return null;
628        for (int i=0; i<keys.length;i+=2) {
629            if (keys[i].equalsIgnoreCase(key)) return keys[i+1];
630        }
631        return null;
632    }
633
634    public final int getNumKeys() {
635        return keys == null ? 0 : keys.length / 2;
636    }
637
638    @Override
639    public final Collection<String> keySet() {
640        String[] keys = this.keys;
641        if (keys == null)
642            return Collections.emptySet();
643        Set<String> result = new HashSet<>(keys.length / 2);
644        for (int i=0; i<keys.length; i+=2) {
645            result.add(keys[i]);
646        }
647        return result;
648    }
649
650    /**
651     * Replies true, if the map of key/value pairs of this primitive is not empty.
652     *
653     * @return true, if the map of key/value pairs of this primitive is not empty; false
654     *   otherwise
655     */
656    @Override
657    public final boolean hasKeys() {
658        return keys != null;
659    }
660
661    /**
662     * Replies true if this primitive has a tag with key <code>key</code>.
663     *
664     * @param key the key
665     * @return true, if his primitive has a tag with key <code>key</code>
666     */
667    public boolean hasKey(String key) {
668        String[] keys = this.keys;
669        if (key == null) return false;
670        if (keys == null) return false;
671        for (int i=0; i< keys.length;i+=2) {
672            if (keys[i].equals(key)) return true;
673        }
674        return false;
675    }
676
677    /**
678     * What to do, when the tags have changed by one of the tag-changing methods.
679     */
680    protected abstract void keysChangedImpl(Map<String, String> originalKeys);
681
682    /**
683     * Replies the name of this primitive. The default implementation replies the value
684     * of the tag <tt>name</tt> or null, if this tag is not present.
685     *
686     * @return the name of this primitive
687     */
688    @Override
689    public String getName() {
690        return get("name");
691    }
692
693    /**
694     * Replies the a localized name for this primitive given by the value of the tags (in this order)
695     * <ul>
696     *   <li>name:lang_COUNTRY_Variant  of the current locale</li>
697     *   <li>name:lang_COUNTRY of the current locale</li>
698     *   <li>name:lang of the current locale</li>
699     *   <li>name of the current locale</li>
700     * </ul>
701     *
702     * null, if no such tag exists
703     *
704     * @return the name of this primitive
705     */
706    @Override
707    public String getLocalName() {
708        final Locale locale = Locale.getDefault();
709        String key = "name:" + locale.toString();
710        String val = get(key);
711        if (val != null)
712            return val;
713
714        final String language = locale.getLanguage();
715        key = "name:" + language + "_" + locale.getCountry();
716        val = get(key);
717        if (val != null)
718            return val;
719
720        key = "name:" + language;
721        val = get(key);
722        if (val != null)
723            return val;
724
725        return getName();
726    }
727
728    /**
729     * Tests whether this primitive contains a tag consisting of {@code key} and {@code values}.
730     * @param key the key forming the tag.
731     * @param value value forming the tag.
732     * @return true iff primitive contains a tag consisting of {@code key} and {@code value}.
733     */
734    public boolean hasTag(String key, String value) {
735        return Objects.equals(value, get(key));
736    }
737
738    /**
739     * Tests whether this primitive contains a tag consisting of {@code key} and any of {@code values}.
740     * @param key the key forming the tag.
741     * @param values one or many values forming the tag.
742     * @return true if primitive contains a tag consisting of {@code key} and any of {@code values}.
743     */
744    public boolean hasTag(String key, String... values) {
745        return hasTag(key, Arrays.asList(values));
746    }
747
748    /**
749     * Tests whether this primitive contains a tag consisting of {@code key} and any of {@code values}.
750     * @param key the key forming the tag.
751     * @param values one or many values forming the tag.
752     * @return true iff primitive contains a tag consisting of {@code key} and any of {@code values}.
753     */
754    public boolean hasTag(String key, Collection<String> values) {
755        return values.contains(get(key));
756    }
757}