001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.properties;
003
004import java.util.ArrayList;
005import java.util.Iterator;
006import java.util.LinkedHashMap;
007import java.util.List;
008import java.util.Map;
009
010import org.openstreetmap.josm.data.osm.Tag;
011import org.openstreetmap.josm.data.osm.search.SearchCompiler;
012import org.openstreetmap.josm.data.osm.search.SearchParseError;
013import org.openstreetmap.josm.data.osm.search.SearchSetting;
014import org.openstreetmap.josm.data.preferences.ListProperty;
015
016/**
017 * Manages list of recently used tags that will be displayed in the {@link PropertiesDialog}.
018 */
019class RecentTagCollection {
020
021    /**
022     * LRU cache for recently added tags (http://java-planet.blogspot.com/2005/08/how-to-set-up-simple-lru-cache-using.html)
023     */
024    static final class LruCache extends LinkedHashMap<Tag, Void> {
025        private final int capacity;
026
027        LruCache(int capacity) {
028            super(capacity + 1, 1.1f, true);
029            this.capacity = capacity;
030        }
031
032        @Override
033        protected boolean removeEldestEntry(Map.Entry<Tag, Void> eldest) {
034            return size() > capacity;
035        }
036    }
037
038    private final Map<Tag, Void> recentTags;
039    private SearchCompiler.Match tagsToIgnore;
040
041    RecentTagCollection(final int capacity) {
042        recentTags = new LruCache(capacity);
043        tagsToIgnore = SearchCompiler.Never.INSTANCE;
044    }
045
046    public void loadFromPreference(ListProperty property) {
047        recentTags.clear();
048        Iterator<String> it = property.get().iterator();
049        while (it.hasNext()) {
050            String key = it.next();
051            String value = it.next();
052            add(new Tag(key, value));
053        }
054    }
055
056    public void saveToPreference(ListProperty property) {
057        List<String> c = new ArrayList<>(recentTags.size() * 2);
058        for (Tag t : recentTags.keySet()) {
059            c.add(t.getKey());
060            c.add(t.getValue());
061        }
062        property.put(c);
063    }
064
065    public void add(Tag tag) {
066        if (!tagsToIgnore.match(tag)) {
067            recentTags.put(tag, null);
068        }
069    }
070
071    public boolean isEmpty() {
072        return recentTags.isEmpty();
073    }
074
075    public List<Tag> toList() {
076        return new ArrayList<>(recentTags.keySet());
077    }
078
079    public void setTagsToIgnore(SearchCompiler.Match tagsToIgnore) {
080        this.tagsToIgnore = tagsToIgnore;
081        recentTags.keySet().removeIf(tagsToIgnore::match);
082    }
083
084    public void setTagsToIgnore(SearchSetting tagsToIgnore) throws SearchParseError {
085        setTagsToIgnore(tagsToIgnore.text.isEmpty() ? SearchCompiler.Never.INSTANCE : SearchCompiler.compile(tagsToIgnore));
086    }
087
088    public SearchSetting ignoreTag(Tag tagToIgnore, SearchSetting settingToUpdate) throws SearchParseError {
089        final String forTag = SearchCompiler.buildSearchStringForTag(tagToIgnore.getKey(), tagToIgnore.getValue());
090        settingToUpdate.text = settingToUpdate.text.isEmpty()
091                ? forTag
092                : settingToUpdate.text + " OR " + forTag;
093        setTagsToIgnore(settingToUpdate);
094        return settingToUpdate;
095    }
096}