001package jmri.jmrit.roster; 002 003import java.beans.PropertyChangeEvent; 004import java.io.File; 005import java.io.FileNotFoundException; 006import java.util.HashMap; 007import java.util.Locale; 008import java.util.Set; 009import java.util.prefs.BackingStoreException; 010import java.util.prefs.Preferences; 011import javax.annotation.CheckForNull; 012import javax.annotation.Nonnull; 013import jmri.implementation.FileLocationsPreferences; 014import jmri.profile.Profile; 015import jmri.profile.ProfileManager; 016import jmri.profile.ProfileUtils; 017import jmri.spi.PreferencesManager; 018import jmri.util.FileUtil; 019import jmri.util.prefs.AbstractPreferencesManager; 020import jmri.util.prefs.InitializationException; 021import org.openide.util.lookup.ServiceProvider; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025/** 026 * Load and store the Roster configuration. 027 * 028 * This only configures the Roster when initialized so that configuration 029 * changes made by users do not affect the running instance of JMRI, but only 030 * take effect after restarting JMRI. 031 * 032 * @author Randall Wood (C) 2015 033 */ 034@ServiceProvider(service = PreferencesManager.class) 035public class RosterConfigManager extends AbstractPreferencesManager { 036 037 private final HashMap<Profile, String> directories = new HashMap<>(); 038 private final HashMap<Profile, String> defaultOwners = new HashMap<>(); 039 private final HashMap<Profile, Roster> rosters = new HashMap<>(); 040 041 public static final String DIRECTORY = "directory"; 042 public static final String DEFAULT_OWNER = "defaultOwner"; 043 private static final Logger log = LoggerFactory.getLogger(RosterConfigManager.class); 044 045 public RosterConfigManager() { 046 log.debug("Roster is {}", this.directories); 047 FileUtil.getDefault().addPropertyChangeListener(FileUtil.PREFERENCES, (PropertyChangeEvent evt) -> { 048 FileUtil.Property oldValue = (FileUtil.Property) evt.getOldValue(); 049 FileUtil.Property newValue = (FileUtil.Property) evt.getNewValue(); 050 Profile project = oldValue.getKey(); 051 log.debug("UserFiles changed from {} to {}", evt.getOldValue(), evt.getNewValue()); 052 if (RosterConfigManager.this.getDirectory(project).equals(oldValue.getValue())) { 053 RosterConfigManager.this.setDirectory(project, newValue.getValue()); 054 } 055 }); 056 } 057 058 @Override 059 public void initialize(Profile profile) throws InitializationException { 060 if (!this.isInitialized(profile)) { 061 Preferences preferences = ProfileUtils.getPreferences(profile, this.getClass(), true); 062 this.setDefaultOwner(profile, preferences.get(DEFAULT_OWNER, this.getDefaultOwner(profile))); 063 try { 064 this.setDirectory(profile, preferences.get(DIRECTORY, this.getDirectory())); 065 } catch (IllegalArgumentException ex) { 066 this.setInitialized(profile, true); 067 String unavailablePath = preferences.get(DIRECTORY, this.getDirectory()); 068 log.warn("Roster location unavailable: {}", unavailablePath); 069 throw new RosterLocationUnavailableException( 070 Bundle.getMessage(Locale.ENGLISH, "IllegalRosterLocation", unavailablePath), 071 ex.getMessage(), 072 unavailablePath, 073 ex); 074 } 075 getRoster(profile).setRosterLocation(this.getDirectory()); 076 this.setInitialized(profile, true); 077 } 078 } 079 080 @Override 081 public void savePreferences(Profile profile) { 082 Preferences preferences = ProfileUtils.getPreferences(profile, this.getClass(), true); 083 preferences.put(DIRECTORY, FileUtil.getPortableFilename(this.getDirectory())); 084 preferences.put(DEFAULT_OWNER, this.getDefaultOwner(profile)); 085 try { 086 preferences.sync(); 087 } catch (BackingStoreException ex) { 088 log.error("Unable to save preferences", ex); 089 } 090 } 091 092 @Override 093 @Nonnull 094 public Set<Class<? extends PreferencesManager>> getRequires() { 095 Set<Class<? extends PreferencesManager>> requires = super.getRequires(); 096 requires.add(FileLocationsPreferences.class); 097 return requires; 098 } 099 100 /** 101 * Get the default owner for the active profile. 102 * 103 * @return the default owner 104 */ 105 @Nonnull 106 public String getDefaultOwner() { 107 return getDefaultOwner(ProfileManager.getDefault().getActiveProfile()); 108 } 109 110 /** 111 * Get the default owner for the specified profile. 112 * 113 * @param profile the profile to get the default owner for 114 * @return the default owner 115 */ 116 @Nonnull 117 public String getDefaultOwner(@CheckForNull Profile profile) { 118 String owner = defaultOwners.get(profile); 119 // defaultOwner should never be null, but check anyway to ensure its not 120 if (owner == null) { 121 owner = ""; // NOI18N 122 defaultOwners.put(profile, owner); 123 } 124 return owner; 125 } 126 127 /** 128 * Set the default owner for the specified profile. 129 * 130 * @param profile the profile to set the default owner for 131 * @param defaultOwner the default owner to set 132 */ 133 public void setDefaultOwner(@CheckForNull Profile profile, @CheckForNull String defaultOwner) { 134 if (defaultOwner == null) { 135 defaultOwner = ""; 136 } 137 String oldDefaultOwner = this.defaultOwners.get(profile); 138 this.defaultOwners.put(profile, defaultOwner); 139 firePropertyChange(DEFAULT_OWNER, oldDefaultOwner, defaultOwner); 140 } 141 142 /** 143 * Get the roster directory for the active profile. 144 * 145 * @return the directory 146 */ 147 @Nonnull 148 public String getDirectory() { 149 return getDirectory(ProfileManager.getDefault().getActiveProfile()); 150 } 151 152 /** 153 * Get the roster directory for the specified profile. 154 * 155 * @param profile the profile to get the directory for 156 * @return the directory 157 */ 158 @Nonnull 159 public String getDirectory(@CheckForNull Profile profile) { 160 String directory = directories.get(profile); 161 if (directory == null) { 162 directory = FileUtil.PREFERENCES; 163 } 164 if (FileUtil.PREFERENCES.equals(directory)) { 165 return FileUtil.getUserFilesPath(); 166 } 167 return directory; 168 } 169 170 /** 171 * Set the roster directory for the specified profile. 172 * 173 * @param profile the profile to set the directory for 174 * @param directory the directory to set 175 */ 176 public void setDirectory(@CheckForNull Profile profile, @CheckForNull String directory) { 177 if (directory == null || directory.isEmpty()) { 178 directory = FileUtil.PREFERENCES; 179 } 180 String oldDirectory = this.directories.get(profile); 181 try { 182 if (!FileUtil.getFile(directory).isDirectory()) { 183 throw new IllegalArgumentException(Bundle.getMessage("IllegalRosterLocation", directory)); // NOI18N 184 } 185 } catch (FileNotFoundException ex) { // thrown by getFile() if directory does not exist 186 throw new IllegalArgumentException(Bundle.getMessage("IllegalRosterLocation", directory)); // NOI18N 187 } 188 if (!directory.equals(FileUtil.PREFERENCES)) { 189 directory = FileUtil.getAbsoluteFilename(directory); 190 if (!directory.endsWith(File.separator)) { 191 directory = directory + File.separator; 192 } 193 } 194 this.directories.put(profile, directory); 195 log.debug("Roster changed from {} to {}", oldDirectory, this.directories); 196 firePropertyChange(DIRECTORY, oldDirectory, directory); 197 } 198 199 /** 200 * Get the roster for the profile. 201 * 202 * @param profile the profile to get the roster for 203 * @return the roster for the profile 204 */ 205 @Nonnull 206 public Roster getRoster(@CheckForNull Profile profile) { 207 Roster roster = rosters.get(profile); 208 if (roster == null) { 209 roster = new Roster(); 210 rosters.put(profile, roster); 211 } 212 return roster; 213 } 214 215 /** 216 * Set the roster for the profile. 217 * 218 * @param profile the profile to set the roster for 219 * @param roster the roster for the profile 220 * @return the roster just set, so this method can be used in a chain 221 */ 222 @Nonnull 223 public Roster setRoster(@CheckForNull Profile profile, @Nonnull Roster roster) { 224 rosters.put(profile, roster); 225 return getRoster(profile); 226 } 227}