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