001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.tagging.ac; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.Set; 008 009import javax.swing.JTable; 010import javax.swing.table.AbstractTableModel; 011 012import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 013import org.openstreetmap.josm.data.tagging.ac.AutoCompletionPriority; 014import org.openstreetmap.josm.data.tagging.ac.AutoCompletionSet; 015import org.openstreetmap.josm.tools.CheckParameterUtil; 016 017/** 018 * AutoCompletionList manages a graphical list of {@link AutoCompletionItem}s. 019 * 020 * The list is sorted, items with higher priority first, then according to lexicographic order 021 * on the value of the {@link AutoCompletionItem}. 022 * 023 * AutoCompletionList maintains two views on the list of {@link AutoCompletionItem}s. 024 * <ol> 025 * <li>the bare, unfiltered view which includes all items</li> 026 * <li>a filtered view, which includes only items which match a current filter expression</li> 027 * </ol> 028 * 029 * AutoCompletionList is an {@link AbstractTableModel} which serves the list of filtered 030 * items to a {@link JTable}. 031 * @since 1762 032 */ 033public class AutoCompletionList extends AbstractTableModel { 034 035 /** the bare list of AutoCompletionItems */ 036 private final transient AutoCompletionSet list; 037 /** the filtered list of AutoCompletionItems */ 038 private final transient ArrayList<AutoCompletionItem> filtered; 039 /** the filter expression */ 040 private String filter; 041 042 /** 043 * constructor 044 */ 045 public AutoCompletionList() { 046 list = new AutoCompletionSet(); 047 filtered = new ArrayList<>(); 048 } 049 050 /** 051 * applies a filter expression to the list of {@link AutoCompletionItem}s. 052 * 053 * The matching criterion is a case insensitive substring match. 054 * 055 * @param filter the filter expression; must not be null 056 * 057 * @throws IllegalArgumentException if filter is null 058 */ 059 public void applyFilter(String filter) { 060 CheckParameterUtil.ensureParameterNotNull(filter, "filter"); 061 this.filter = filter; 062 filter(); 063 } 064 065 /** 066 * clears the current filter 067 */ 068 public void clearFilter() { 069 filter = null; 070 filter(); 071 } 072 073 /** 074 * @return the current filter expression; null, if no filter expression is set 075 */ 076 public String getFilter() { 077 return filter; 078 } 079 080 /** 081 * adds an {@link AutoCompletionItem} to the list. Only adds the item if it 082 * is not null and if not in the list yet. 083 * 084 * @param item the item 085 * @since 12859 086 */ 087 public void add(AutoCompletionItem item) { 088 if (item != null && list.add(item)) { 089 filter(); 090 } 091 } 092 093 /** 094 * adds another {@link AutoCompletionList} to this list. An item is only 095 * added it is not null and if it does not exist in the list yet. 096 * 097 * @param other another auto completion list; must not be null 098 * @throws IllegalArgumentException if other is null 099 */ 100 public void add(AutoCompletionList other) { 101 CheckParameterUtil.ensureParameterNotNull(other, "other"); 102 add(other.list); 103 } 104 105 /** 106 * adds a colleciton of {@link AutoCompletionItem} to this list. An item is only 107 * added it is not null and if it does not exist in the list yet. 108 * 109 * @param collection auto completion collection; must not be null 110 * @throws IllegalArgumentException if other is null 111 * @since 12859 112 */ 113 public void add(Collection<AutoCompletionItem> collection) { 114 CheckParameterUtil.ensureParameterNotNull(collection, "collection"); 115 if (list.addAll(collection)) { 116 filter(); 117 } 118 } 119 120 /** 121 * adds a list of strings to this list. Only strings which 122 * are not null and which do not exist yet in the list are added. 123 * 124 * @param values a list of strings to add 125 * @param priority the priority to use 126 * @since 12859 127 */ 128 public void add(Collection<String> values, AutoCompletionPriority priority) { 129 if (values != null && list.addAll(values, priority)) { 130 filter(); 131 } 132 } 133 134 /** 135 * Adds values that have been entered by the user. 136 * @param values values that have been entered by the user 137 */ 138 public void addUserInput(Collection<String> values) { 139 if (values != null && list.addUserInput(values)) { 140 filter(); 141 } 142 } 143 144 /** 145 * checks whether a specific item is already in the list. Matches for the 146 * the value <strong>and</strong> the priority of the item 147 * 148 * @param item the item to check 149 * @return true, if item is in the list; false, otherwise 150 * @since 12859 151 */ 152 public boolean contains(AutoCompletionItem item) { 153 return list.contains(item); 154 } 155 156 /** 157 * checks whether an item with the given value is already in the list. Ignores 158 * priority of the items. 159 * 160 * @param value the value of an auto completion item 161 * @return true, if value is in the list; false, otherwise 162 */ 163 public boolean contains(String value) { 164 return list.contains(value); 165 } 166 167 /** 168 * removes the auto completion item with key <code>key</code> 169 * @param key the key 170 */ 171 public void remove(String key) { 172 if (key != null) { 173 list.remove(key); 174 } 175 } 176 177 protected void filter() { 178 filtered.clear(); 179 if (filter == null) { 180 // Collections.copy throws an exception "Source does not fit in dest" 181 filtered.ensureCapacity(list.size()); 182 filtered.addAll(list); 183 return; 184 } 185 186 // apply the pattern to list of possible values. If it matches, add the 187 // value to the list of filtered values 188 // 189 list.stream().filter(e -> e.getValue().startsWith(filter)).forEach(filtered::add); 190 fireTableDataChanged(); 191 } 192 193 /** 194 * replies the number of filtered items 195 * 196 * @return the number of filtered items 197 */ 198 public int getFilteredSize() { 199 return filtered.size(); 200 } 201 202 /** 203 * replies the idx-th item from the list of filtered items 204 * @param idx the index; must be in the range 0 <= idx < {@link #getFilteredSize()} 205 * @return the item 206 * 207 * @throws IndexOutOfBoundsException if idx is out of bounds 208 * @since 12859 209 */ 210 public AutoCompletionItem getFilteredItemAt(int idx) { 211 return filtered.get(idx); 212 } 213 214 AutoCompletionSet getSet() { 215 return list; 216 } 217 218 Set<AutoCompletionItem> getUnmodifiableSet() { 219 return Collections.unmodifiableSet(list); 220 } 221 222 /** 223 * removes all elements from the auto completion list 224 */ 225 public void clear() { 226 list.clear(); 227 fireTableDataChanged(); 228 } 229 230 @Override 231 public int getColumnCount() { 232 return 1; 233 } 234 235 @Override 236 public int getRowCount() { 237 return list == null ? 0 : getFilteredSize(); 238 } 239 240 @Override 241 public Object getValueAt(int rowIndex, int columnIndex) { 242 return list == null ? null : getFilteredItemAt(rowIndex); 243 } 244}