001package jmri.jmrit.display; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006import java.util.stream.Collectors; 007 008import javax.annotation.CheckForNull; 009import javax.annotation.Nonnull; 010import jmri.InstanceManager; 011import jmri.InstanceManagerAutoDefault; 012import jmri.beans.Bean; 013 014/** 015 * Manager for JMRI Editors. This manager tracks editors, extending the Set 016 * interface to do so (so it can be interacted with as a normal set), while also 017 * providing some methods specific to editors. 018 * <p> 019 * This manager listens to the {@code title} property of Editors to be notified 020 * to changes to the title of the Editor that could affect the order of editors. 021 * <p> 022 * This manager generates an {@link java.beans.IndexedPropertyChangeEvent} for 023 * the property named {@code editors} when an editor is added or removed and 024 * forwards the {@link java.beans.PropertyChangeEvent} for the {@code title} 025 * property of Editors in the manager. 026 * 027 * @author Randall Wood Copyright 2020 028 */ 029public class EditorManager extends Bean implements PropertyChangeListener, InstanceManagerAutoDefault { 030 031 public static final String EDITORS = "editors"; 032 public static final String TITLE = "title"; 033 private final SortedSet<Editor> set = Collections.synchronizedSortedSet(new TreeSet<>(Comparator.comparing(Editor::getTitle))); 034 035 public EditorManager() { 036 super(false); 037 } 038 039 /** 040 * Set the title for the Preferences / Messages tab. 041 * Called by JmriUserPreferencesManager. 042 * @return the title string. 043 */ 044 public String getClassDescription() { 045 return Bundle.getMessage("TitlePanelDialogs"); // NOI18N 046 } 047 048 /** 049 * Set the details for Preferences / Messages tab. 050 * Called by JmriUserPreferencesManager. 051 * <p> 052 * The dialogs are in jmri.configurexml.LoadXmlConfigAction and jmri.jmrit.display.Editor. 053 * They are anchored here since the preferences system appears to need a class that can instantiated. 054 */ 055 public void setMessagePreferencesDetails() { 056 InstanceManager.getDefault(jmri.UserPreferencesManager.class).setPreferenceItemDetails( 057 "jmri.jmrit.display.EditorManager", "skipHideDialog", Bundle.getMessage("PanelHideSkip")); // NOI18N 058 InstanceManager.getDefault(jmri.UserPreferencesManager.class).setPreferenceItemDetails( 059 "jmri.jmrit.display.EditorManager", "skipDupLoadDialog", Bundle.getMessage("DuplicateLoadSkip")); // NOI18N 060 } 061 062 /** 063 * Add an editor to this manager. 064 * 065 * @param editor the editor to add 066 */ 067 public void add(@Nonnull Editor editor) { 068 boolean result = set.add(editor); 069 if (result) { 070 fireIndexedPropertyChange(EDITORS, set.size(), null, editor); 071 editor.addPropertyChangeListener(TITLE, this); 072 } 073 } 074 075 /** 076 * Check if an editor is in the manager. 077 * 078 * @param editor the editor to check for 079 * @return true if this manager contains an editor with name; false 080 * otherwise 081 */ 082 public boolean contains(@Nonnull Editor editor) { 083 return set.contains(editor); 084 } 085 086 /** 087 * Get all managed editors. This set is sorted by the title of the editor. 088 * 089 * @return the set of all editors 090 */ 091 @Nonnull 092 public SortedSet<Editor> getAll() { 093 return new TreeSet<>(set); 094 } 095 096 /** 097 * Get all managed editors that implement the specified type. This set is 098 * sorted by the title of the editor. 099 * 100 * @param <T> the specified type 101 * @param type the specified type 102 * @return the set of all editors of the specified type 103 */ 104 @Nonnull 105 public <T extends Editor> SortedSet<T> getAll(@Nonnull Class<T> type) { 106 return set.stream() 107 .filter(e -> type.isAssignableFrom(e.getClass())) 108 .map(type::cast) 109 .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Editor::getTitle)))); 110 } 111 112 /** 113 * Get the editor with the given title. 114 * 115 * @param title the title of the editor 116 * @return the editor with the given title or null if no editor by that title 117 * exists 118 */ 119 @CheckForNull 120 public Editor get(@Nonnull String title) { 121 return getAll().stream().filter(e -> e.getTitle().equals(title)).findFirst().orElse(null); 122 } 123 124 /** 125 * Get the editor with the given name. 126 * 127 * @param name the name of the editor 128 * @return the editor with the given name or null if no editor by that name 129 * exists 130 */ 131 @CheckForNull 132 public Editor getByName(@Nonnull String name) { 133 return getAll().stream().filter(e -> e.getName().equals(name)).findFirst().orElse(null); 134 } 135 136 /** 137 * Get the editor with the given name or the editor with the given target frame name. 138 * 139 * @param name the name of the editor or target frame 140 * @return the editor or null 141 */ 142 @CheckForNull 143 public Editor getTargetFrame(@Nonnull String name) { 144 Editor editor = get(name); 145 if (editor != null) { 146 return editor; 147 } 148 return getAll().stream().filter(e -> e.getTargetFrame().getTitle().equals(name)).findFirst().orElse(null); 149 } 150 151 /** 152 * Get the editor with the given name and type. 153 * 154 * @param <T> the type of the editor 155 * @param type the type of the editor 156 * @param name the name of the editor 157 * @return the editor with the given name or null if no editor by that name 158 * exists 159 */ 160 @CheckForNull 161 public <T extends Editor> T get(@Nonnull Class<T> type, @Nonnull String name) { 162 return type.cast(set.stream() 163 .filter(e -> e.getClass().isAssignableFrom(type) && e.getTitle().equals(name)) 164 .findFirst().orElse(null)); 165 } 166 167 /** 168 * Remove an editor from this manager. 169 * 170 * @param editor the editor to remove 171 */ 172 public void remove(@Nonnull Editor editor) { 173 boolean result = set.remove(editor); 174 if (result) { 175 fireIndexedPropertyChange(EDITORS, set.size(), editor, null); 176 editor.removePropertyChangeListener(TITLE, this); 177 } 178 } 179 180 @Override 181 public void propertyChange(PropertyChangeEvent evt) { 182 if (evt.getSource() instanceof Editor) { 183 Editor editor = (Editor) evt.getSource(); 184 if (contains(editor) && TITLE.equals(evt.getPropertyName())) { 185 set.remove(editor); 186 set.add(editor); 187 firePropertyChange(evt); 188 } 189 } 190 } 191 192 /** 193 * Check if an editor with the specified name is in the manager. 194 * 195 * @param name the name to check for 196 * @return true if this manager contains an editor with name; false 197 * otherwise 198 */ 199 public boolean contains(String name) { 200 return get(name) != null; 201 } 202 203 /** 204 * Get the set of all Editors as a List. This is a convenience method for 205 * use in scripts. 206 * 207 * @return the set of all Editors 208 */ 209 @Nonnull 210 public List<Editor> getList() { 211 return new ArrayList<>(getAll()); 212 } 213 214 /** 215 * Get the set of all editors that implement the specified type. This is a 216 * convenience method for use in scripts. 217 * 218 * @param <T> the specified type 219 * @param type the specified type 220 * @return the set of all editors that implement the specified type 221 */ 222 @Nonnull 223 public <T extends Editor> List<T> getList(@Nonnull Class<T> type) { 224 return new ArrayList<>(getAll(type)); 225 } 226}