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