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}