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.HashMap; 008import java.util.List; 009import java.util.Map; 010 011import javax.swing.JTable; 012import javax.swing.table.AbstractTableModel; 013 014import org.openstreetmap.josm.tools.CheckParameterUtil; 015 016/** 017 * AutoCompletionList manages a list of {@link AutoCompletionListItem}s. 018 * 019 * The list is sorted, items with higher priority first, then according to lexicographic order 020 * on the value of the {@link AutoCompletionListItem}. 021 * 022 * AutoCompletionList maintains two views on the list of {@link AutoCompletionListItem}s. 023 * <ol> 024 * <li>the bare, unfiltered view which includes all items</li> 025 * <li>a filtered view, which includes only items which match a current filter expression</li> 026 * </ol> 027 * 028 * AutoCompletionList is an {@link AbstractTableModel} which serves the list of filtered 029 * items to a {@link JTable}. 030 * 031 */ 032public class AutoCompletionList extends AbstractTableModel { 033 034 /** the bare list of AutoCompletionItems */ 035 private List<AutoCompletionListItem> list = null; 036 /** the filtered list of AutoCompletionItems */ 037 private ArrayList<AutoCompletionListItem> filtered = null; 038 /** the filter expression */ 039 private String filter = null; 040 /** map from value to priority */ 041 private Map<String,AutoCompletionListItem> valutToItemMap; 042 043 /** 044 * constructor 045 */ 046 public AutoCompletionList() { 047 list = new ArrayList<>(); 048 filtered = new ArrayList<>(); 049 valutToItemMap = new HashMap<>(); 050 } 051 052 /** 053 * applies a filter expression to the list of {@link AutoCompletionListItem}s. 054 * 055 * The matching criterion is a case insensitive substring match. 056 * 057 * @param filter the filter expression; must not be null 058 * 059 * @exception IllegalArgumentException thrown, if filter is null 060 */ 061 public void applyFilter(String filter) { 062 CheckParameterUtil.ensureParameterNotNull(filter, "filter"); 063 this.filter = filter; 064 filter(); 065 } 066 067 /** 068 * clears the current filter 069 * 070 */ 071 public void clearFilter() { 072 filter = null; 073 filter(); 074 } 075 076 /** 077 * @return the current filter expression; null, if no filter expression is set 078 */ 079 public String getFilter() { 080 return filter; 081 } 082 083 /** 084 * adds an AutoCompletionListItem to the list. Only adds the item if it 085 * is not null and if not in the list yet. 086 * 087 * @param item the item 088 */ 089 public void add(AutoCompletionListItem item) { 090 if (item == null) 091 return; 092 appendOrUpdatePriority(item); 093 sort(); 094 filter(); 095 } 096 097 /** 098 * adds another AutoCompletionList to this list. An item is only 099 * added it is not null and if it does not exist in the list yet. 100 * 101 * @param other another auto completion list; must not be null 102 * @exception IllegalArgumentException thrown, if other is null 103 */ 104 public void add(AutoCompletionList other) { 105 CheckParameterUtil.ensureParameterNotNull(other, "other"); 106 for (AutoCompletionListItem item : other.list) { 107 appendOrUpdatePriority(item); 108 } 109 sort(); 110 filter(); 111 } 112 113 /** 114 * adds a list of AutoCompletionListItem to this list. Only items which 115 * are not null and which do not exist yet in the list are added. 116 * 117 * @param other a list of AutoCompletionListItem; must not be null 118 * @exception IllegalArgumentException thrown, if other is null 119 */ 120 public void add(List<AutoCompletionListItem> other) { 121 CheckParameterUtil.ensureParameterNotNull(other, "other"); 122 for (AutoCompletionListItem toadd : other) { 123 appendOrUpdatePriority(toadd); 124 } 125 sort(); 126 filter(); 127 } 128 129 /** 130 * adds a list of strings to this list. Only strings which 131 * are not null and which do not exist yet in the list are added. 132 * 133 * @param values a list of strings to add 134 * @param priority the priority to use 135 */ 136 public void add(Collection<String> values, AutoCompletionItemPriority priority) { 137 if (values == null) return; 138 for (String value: values) { 139 if (value == null) { 140 continue; 141 } 142 AutoCompletionListItem item = new AutoCompletionListItem(value,priority); 143 appendOrUpdatePriority(item); 144 145 } 146 sort(); 147 filter(); 148 } 149 150 public void addUserInput(Collection<String> values) { 151 if (values == null) return; 152 int i = 0; 153 for (String value: values) { 154 if (value == null) { 155 continue; 156 } 157 AutoCompletionListItem item = new AutoCompletionListItem(value, new AutoCompletionItemPriority(false, false, false, i)); 158 appendOrUpdatePriority(item); 159 i++; 160 } 161 sort(); 162 filter(); 163 } 164 165 protected void appendOrUpdatePriority(AutoCompletionListItem toAdd) { 166 AutoCompletionListItem item = valutToItemMap.get(toAdd.getValue()); 167 if (item == null) { 168 // new item does not exist yet. Add it to the list 169 list.add(toAdd); 170 valutToItemMap.put(toAdd.getValue(), toAdd); 171 } else { 172 item.setPriority(item.getPriority().mergeWith(toAdd.getPriority())); 173 } 174 } 175 176 /** 177 * checks whether a specific item is already in the list. Matches for the 178 * the value <strong>and</strong> the priority of the item 179 * 180 * @param item the item to check 181 * @return true, if item is in the list; false, otherwise 182 */ 183 public boolean contains(AutoCompletionListItem item) { 184 if (item == null) 185 return false; 186 return list.contains(item); 187 } 188 189 /** 190 * checks whether an item with the given value is already in the list. Ignores 191 * priority of the items. 192 * 193 * @param value the value of an auto completion item 194 * @return true, if value is in the list; false, otherwise 195 */ 196 public boolean contains(String value) { 197 if (value == null) 198 return false; 199 for (AutoCompletionListItem item: list) { 200 if (item.getValue().equals(value)) 201 return true; 202 } 203 return false; 204 } 205 206 /** 207 * removes the auto completion item with key <code>key</code> 208 * @param key the key; 209 */ 210 public void remove(String key) { 211 if (key == null) 212 return; 213 for (int i=0;i< list.size();i++) { 214 AutoCompletionListItem item = list.get(i); 215 if (item.getValue().equals(key)) { 216 list.remove(i); 217 return; 218 } 219 } 220 } 221 222 /** 223 * sorts the list 224 */ 225 protected void sort() { 226 Collections.sort(list); 227 } 228 229 protected void filter() { 230 filtered.clear(); 231 if (filter == null) { 232 // Collections.copy throws an exception "Source does not fit in dest" 233 // Collections.copy(filtered, list); 234 filtered.ensureCapacity(list.size()); 235 for (AutoCompletionListItem item: list) { 236 filtered.add(item); 237 } 238 return; 239 } 240 241 // apply the pattern to list of possible values. If it matches, add the 242 // value to the list of filtered values 243 // 244 for (AutoCompletionListItem item : list) { 245 if (item.getValue().startsWith(filter)) { 246 filtered.add(item); 247 } 248 } 249 fireTableDataChanged(); 250 } 251 252 /** 253 * replies the number of filtered items 254 * 255 * @return the number of filtered items 256 */ 257 public int getFilteredSize() { 258 return this.filtered.size(); 259 } 260 261 /** 262 * replies the idx-th item from the list of filtered items 263 * @param idx the index; must be in the range 0 <= idx < {@link #getFilteredSize()} 264 * @return the item 265 * 266 * @exception IndexOutOfBoundsException thrown, if idx is out of bounds 267 */ 268 public AutoCompletionListItem getFilteredItem(int idx) { 269 if (idx < 0 || idx >= getFilteredSize()) 270 throw new IndexOutOfBoundsException("idx out of bounds. idx=" + idx); 271 return filtered.get(idx); 272 } 273 274 List<AutoCompletionListItem> getList() { 275 return list; 276 } 277 278 List<AutoCompletionListItem> getUnmodifiableList() { 279 return Collections.unmodifiableList(list); 280 } 281 282 /** 283 * removes all elements from the auto completion list 284 * 285 */ 286 public void clear() { 287 valutToItemMap.clear(); 288 list.clear(); 289 fireTableDataChanged(); 290 } 291 292 @Override 293 public int getColumnCount() { 294 return 1; 295 } 296 297 @Override 298 public int getRowCount() { 299 300 return list == null ? 0 : getFilteredSize(); 301 } 302 303 @Override 304 public Object getValueAt(int rowIndex, int columnIndex) { 305 return list == null ? null : getFilteredItem(rowIndex); 306 } 307}