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