Class Roster
- All Implemented Interfaces:
PropertyChangeListener,EventListener,PropertyChangeProvider,RosterGroupSelector
It works with the "roster-config" XML schema to load and store its information.
This is an in-memory representation of the roster xml file (see below for constants defining name and location). As such, this class is also responsible for the "dirty bit" handling to ensure it gets written. As a temporary reliability enhancement, all changes to this structure are now being written to a backup file, and a copy is made when the file is opened.
Multiple Roster objects don't make sense, so we use an "instance" member to navigate to a single one.
The only bound property is the list of RosterEntrys; a PropertyChangedEvent is fired every time that changes.
The entries are stored in an ArrayList, sorted alphabetically. That sort is done manually each time an entry is added.
The roster is stored in a "Roster Index", which can be read or written. Each individual entry (once stored) contains a filename which can be used to retrieve the locomotive information for that roster entry. Note that the RosterEntry information is duplicated in both the Roster (stored in the roster.xml file) and in the specific file for the entry.
Originally, JMRI managed just one global roster, held in a global Roster object. With the rise of more complicated layouts, code has been added to address multiple rosters, with the primary one now held in Roster.default(). We're moving references to Roster.default() out to the using code, so that eventually we can make those explicit references to other Roster objects as/when needed.
- See Also:
-
Nested Class Summary
Nested classes/interfaces inherited from class jmri.jmrit.XmlFile
XmlFile.Validate -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringName for the property change fired when adding a roster entry.static final StringTitle of the "All Entries" roster group.static final StringName for the property change fired when changing the ID of a roster entry.static final StringName of the default roster index file.static final StringTitle of the "No Group" roster group.static final StringName for the property change fired when removing a roster entry.static final StringProperty change fired when adding a roster group.static final StringString prefixed to roster group names in the roster entry XML.static final StringProperty change fired when removing a roster group.static final StringProperty change fired when renaming a roster group.static final StringProperty change event fired when saving the roster.static final StringFields inherited from class jmri.jmrit.XmlFile
dtdLocation, xsltLocationFields inherited from interface jmri.jmrit.roster.rostergroup.RosterGroupSelector
SELECTED_ROSTER_GROUP -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidAdd a RosterEntry object to the in-memory Roster.voidAdd aPropertyChangeListenerto the listener list.voidaddPropertyChangeListener(String propertyName, PropertyChangeListener listener) Add aPropertyChangeListenerfor a specific property.voidaddRosterGroup(String rg) Add a roster group, notifying all listeners of the change.voidAdd a roster group, notifying all listeners of the change.voidaddRosterGroups(List<RosterGroup> groups) Add a list ofRosterGroup.static StringallEntries(Locale locale) Get the identifier for all entries in the roster.booleancheckEntry(int i, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) Check if an entry is consistent with up to 9 specific properties.booleancheckEntry(List<RosterEntry> list, int i, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) Check if an item from a list of Roster Entry items is consistent with up to 10 specific properties.booleancheckEntry(RosterEntry r, String dccAddress, String decoderModel, String decoderFamily, String productID, String progMode) Check if an entry is consistent with up to 5 specific LNCV properties.booleancheckEntry(RosterEntry r, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group, String developerID, String manufacturerID, String productID) Check if an entry is consistent with up to 12 specific (LNSV2/LNCV) properties.voidcopyRosterGroupList(String oldName, String newName) Copy a roster group, adding every entry in the roster group to the new group.voidDelete a roster group, notifying all listeners of the change.voiddispose()entryFromTitle(String title) Return RosterEntry from a "title" string, ala selection in matchingComboBox.voidNotify that the ID of an entry has changed.fileFromTitle(String title) Return filename from a "title" string, ala selection in matchingComboBox.protected voidfirePropertyChange(String p, Object old, Object n) Get all roster entries.(package private) static String[]Get an array of all the RosterEntry-containing files in the target directory.static RosterGet the roster for the profile returned byProfileManager.getActiveProfile().Return a list of RosterEntry items which have a particular DCC address.getEntriesInGroup(String group) getEntriesMatchingCriteria(String dccAddress, String decoderModel, String decoderFamily, String productID, String progMode) Get a List ofRosterEntryobjects in Roster matching 5 selectors.getEntriesMatchingCriteria(String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) Get a List ofRosterEntryobjects in Roster matching 8 selectors.getEntriesMatchingCriteria(String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group, String developerID, String manufacturerID, String productID) Get a List ofRosterEntryobjects in Roster matching 11 selectors.getEntriesWithAttributeKeyValue(String key, String value) getEntry(int i) Return a specific entry by indexgetEntryForId(String id) Return RosterEntry from an "id" string.getGroupEntry(String group, int i) Get the Nth RosterEntry in the groupintgetGroupIndex(String group, RosterEntry re) (package private) RosterEntrygetNoGroupEntry(int i) (package private) List<RosterEntry>Get allPropertyChangeListeners currently attached to this object.getPropertyChangeListeners(String propertyName) Get allPropertyChangeListeners currently listening to changes to the specified property.static RosterGet the roster for the specified profile.Get a list of the user defined roster group names.static StringgetRosterGroupName(String rosterGroup) static StringgetRosterGroupProperty(String name) Get the string for a RosterGroup property in a RosterEntryGet the groups known to the roster itself.Absolute path to roster file location.Get the default roster group.(package private) booleanisDirty()static StringmakeValidFilename(String entry) Name a valid roster entry filename from an entry name.matchingList(String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id) Get a List ofRosterEntryobjects in Roster matching 7 basic selectors.intintnumGroupEntries(String group) (package private) intvoid(package private) voidRead the contents of a roster XML file into this object.voidreindex()Rebuild the Roster index and store it.voidUpdate the in-memory Roster to be consistent with the current roster file.voidremapRosterGroup(RosterGroup group, String newKey) Changes the key used to look up a RosterGroup by name.voidRemove a RosterEntry object from the in-memory Roster.voidRemove the specified listener from this object.voidremovePropertyChangeListener(String propertyName, PropertyChangeListener listener) Remove the specified listener of the specified property from this object.voidvoidrenameRosterGroupList(String oldName, String newName) Rename a roster group, while keeping every entry in the roster group.voidrosterGroupRenamed(String oldName, String newName) voidsetDefaultRosterGroup(String defaultRosterGroup) (package private) voidsetDirty(boolean b) voidsetRosterIndexFileName(String fileName) voidSet the default location for the Roster file, and all individual locomotive files.(package private) voidWrite the entire roster to a file object.(package private) voidWrite the entire roster to a file.voidStore the roster in the default place, including making a backup if needed.Methods inherited from class jmri.jmrit.XmlFile
addDefaultInfo, backupFileName, checkFile, createFileNameWithDate, dumpElement, findFile, getBuilder, getDefaultDtdLocation, getDefaultValidate, getDtdLocation, getProcessingInstructionHRef, getProcessingInstructionType, getRoot, getValidate, makeBackupFile, makeBackupFile, newDocument, newDocument, revertBackupFile, rootFromFile, rootFromInputStream, rootFromName, rootFromURL, setDefaultDtdLocation, setDefaultValidate, setDtdLocation, setValidate, userFileChooser, userFileChooser, writeXML, xmlDir
-
Field Details
-
schemaVersion
- See Also:
-
DEFAULT_ROSTER_INDEX
Name of the default roster index file. "roster.xml"- See Also:
-
ADD
Name for the property change fired when adding a roster entry. "add"- See Also:
-
REMOVE
Name for the property change fired when removing a roster entry. "remove"- See Also:
-
CHANGE
Name for the property change fired when changing the ID of a roster entry. "change"- See Also:
-
SAVED
Property change event fired when saving the roster. "saved"- See Also:
-
ROSTER_GROUP_ADDED
Property change fired when adding a roster group. "RosterGroupAdded"- See Also:
-
ROSTER_GROUP_REMOVED
Property change fired when removing a roster group. "RosterGroupRemoved"- See Also:
-
ROSTER_GROUP_RENAMED
Property change fired when renaming a roster group. "RosterGroupRenamed"- See Also:
-
ROSTER_GROUP_PREFIX
String prefixed to roster group names in the roster entry XML. "RosterGroup:"- See Also:
-
ALLENTRIES
Title of the "All Entries" roster group. As this varies by locale, do not rely on being able to store this value. -
NOGROUP
Title of the "No Group" roster group. As this varies by locale, do not rely on being able to store this value.
-
-
Constructor Details
-
Roster
public Roster()Create a roster with default contents. -
Roster
-
-
Method Details
-
getDefault
Get the roster for the profile returned byProfileManager.getActiveProfile().- Returns:
- the roster for the active profile
-
getRoster
Get the roster for the specified profile.- Parameters:
profile- the Profile to get the roster for- Returns:
- the roster for the profile
-
addEntry
Add a RosterEntry object to the in-memory Roster.This method notifies the UI of changes so should not be used when adding or reloading many roster entries at once.
- Parameters:
e- Entry to add
-
removeEntry
Remove a RosterEntry object from the in-memory Roster. This does not delete the file for the RosterEntry!- Parameters:
e- Entry to remove
-
numEntries
- Returns:
- number of entries in the roster
-
numGroupEntries
- Parameters:
group- The group being queried or null for all entries in the roster.- Returns:
- The Number of roster entries in the specified group or 0 if the group does not exist.
-
numNoGroupEntries
int numNoGroupEntries() -
entryFromTitle
Return RosterEntry from a "title" string, ala selection in matchingComboBox.- Parameters:
title- The title for the RosterEntry.- Returns:
- The matching RosterEntry or null
-
getEntryForId
Return RosterEntry from an "id" string.- Parameters:
id- The id for the RosterEntry.- Returns:
- The matching RosterEntry or null
-
getEntriesByDccAddress
Return a list of RosterEntry items which have a particular DCC address.- Parameters:
a- The address.- Returns:
- a List of matching entries, empty if there are no matches.
-
getEntry
Return a specific entry by index- Parameters:
i- The RosterEntry at position i in the roster.- Returns:
- The matching RosterEntry
-
getAllEntries
Get all roster entries.- Returns:
- a list of roster entries; the list is empty if the roster is empty
-
getGroupEntry
Get the Nth RosterEntry in the group- Parameters:
group- The group being queried.i- The index within the group of the requested entry.- Returns:
- The specified entry in the group or null if i is larger than the group, or the group does not exist.
-
getNoGroupEntry
-
getNoGroupList
-
getGroupIndex
-
fileFromTitle
Return filename from a "title" string, ala selection in matchingComboBox.- Parameters:
title- The title for the entry.- Returns:
- The filename for the RosterEntry matching title, or null if no such RosterEntry exists.
-
getEntriesWithAttributeKey
-
getEntriesWithAttributeKeyValue
-
getAllAttributeKeys
-
getEntriesInGroup
-
matchingList
@Nonnull public List<RosterEntry> matchingList(String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id) Get a List ofRosterEntryobjects in Roster matching 7 basic selectors. The list will be empty if there are no matches.This method calls
getEntriesMatchingCriteria(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)with a null group.- Parameters:
roadName- road name of entry or null for any road nameroadNumber- road number of entry of null for any numberdccAddress- address of entry or null for any addressmfg- manufacturer of entry or null for any manufacturerdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyid- id (unique name) of entry or null for any id- Returns:
- List of matching RosterEntries or an empty List
- See Also:
-
getEntriesMatchingCriteria
@Nonnull public List<RosterEntry> getEntriesMatchingCriteria(String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group, String developerID, String manufacturerID, String productID) Get a List ofRosterEntryobjects in Roster matching 11 selectors. The list will be empty if there are no matches.- Parameters:
roadName- road name of entry or null for any road nameroadNumber- road number of entry of null for any numberdccAddress- address of entry or null for any addressmfg- manufacturer of entry or null for any manufacturerdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyid- id of entry or null for any idgroup- group entry is member of or null for any groupdeveloperID- developerID of entry, or null for any developerIDmanufacturerID- manufacturerID of entry, or null for any manufacturerIDproductID- productID of entry, or null for any productID- Returns:
- List of matching RosterEntries or an empty List
-
getEntriesMatchingCriteria
@Nonnull public List<RosterEntry> getEntriesMatchingCriteria(String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) Get a List ofRosterEntryobjects in Roster matching 8 selectors. The list will be empty if there are no matches.- Parameters:
roadName- road name of entry or null for any road nameroadNumber- road number of entry of null for any numberdccAddress- address of entry or null for any addressmfg- manufacturer of entry or null for any manufacturerdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyid- id of entry or null for any idgroup- group entry is member of or null for any group- Returns:
- List of matching RosterEntries or an empty List
-
getEntriesMatchingCriteria
@Nonnull public List<RosterEntry> getEntriesMatchingCriteria(String dccAddress, String decoderModel, String decoderFamily, String productID, String progMode) Get a List ofRosterEntryobjects in Roster matching 5 selectors. The list will be empty if there are no matches.This pattern is used for LocoNet LNCV.
- Parameters:
dccAddress- address of entry or null for any addressdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyproductID- decoder productID or null for any productIDprogMode- decoder programming mode- Returns:
- List of matching RosterEntries or an empty List
-
checkEntry
public boolean checkEntry(int i, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) Check if an entry is consistent with up to 9 specific properties.A null String argument always matches. Strings are used for convenience in GUI building.
- Parameters:
i- index for the RosterEntry in the RosterroadName- road name of entry or null for any road nameroadNumber- road number of entry of null for any numberdccAddress- address of entry or null for any addressmfg- manufacturer of entry or null for any manufacturerdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyid- id of entry or null for any idgroup- group entry is member of or null for any group- Returns:
- true if the entry matches
-
checkEntry
public boolean checkEntry(List<RosterEntry> list, int i, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) Check if an item from a list of Roster Entry items is consistent with up to 10 specific properties.A null String argument always matches. Strings are used for convenience in GUI building.
- Parameters:
list- the list of RosterEntry items being searchedi- the index of the roster entry in the listroadName- road name of entry or null for any road nameroadNumber- road number of entry of null for any numberdccAddress- address of entry or null for any addressmfg- manufacturer of entry or null for any manufacturerdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyid- id of entry or null for any idgroup- group entry is member of or null for any group- Returns:
- True if the entry matches
-
checkEntry
public boolean checkEntry(RosterEntry r, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group, String developerID, String manufacturerID, String productID) Check if an entry is consistent with up to 12 specific (LNSV2/LNCV) properties.A null String argument always matches. Strings are used for convenience in GUI building.
- Parameters:
r- the roster entry being checkedroadName- road name of entry or null for any road nameroadNumber- road number of entry of null for any numberdccAddress- address of entry or null for any addressmfg- manufacturer of entry or null for any manufacturerdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyid- id of entry or null for any idgroup- group entry is member of or null for any groupdeveloperID- developerID of entry, or null for any developerIDmanufacturerID- manufacturerID of entry, or null for any manufacturerIDproductID- productID of entry, or null for any productID- Returns:
- True if the entry matches
-
checkEntry
public boolean checkEntry(RosterEntry r, String dccAddress, String decoderModel, String decoderFamily, String productID, String progMode) Check if an entry is consistent with up to 5 specific LNCV properties.A null String argument always matches. Strings are used for convenience in GUI building.
- Parameters:
r- the roster entry being checkeddccAddress- address of entry or null for any addressdecoderModel- decoder model of entry or null for any modeldecoderFamily- decoder family of entry or null for any familyproductID- productId of entry or null for any productIDprogMode- programming mode- Returns:
- True if the entry matches
-
writeFile
Write the entire roster to a file.Creates a new file with the given name, and then calls writeFile (File) to perform the actual work.
- Parameters:
name- Filename for new file, including path info as needed.- Throws:
FileNotFoundException- if file does not existIOException- if unable to write file
-
writeFile
Write the entire roster to a file object. This does not do backup; that has to be done separately. See writeRosterFile() for a public function that finds the default location, does a backup and then calls this.- Parameters:
file- the file to write to- Throws:
IOException- if unable to write file
-
makeValidFilename
Name a valid roster entry filename from an entry name.- Replaces all problematic characters with "_".
- Append .xml suffix
- Parameters:
entry- the getId() entry name from the RosterEntry- Returns:
- Filename for RosterEntry
- Throws:
IllegalArgumentException- if called with null or empty entry name- Since:
- 2.1.5
- See Also:
-
readFile
Read the contents of a roster XML file into this object.Note that this does not clear any existing entries.
- Parameters:
name- filename of roster file- Throws:
org.jdom2.JDOMException- if file is invalid XMLIOException- if unable to read file
-
setDirty
-
isDirty
boolean isDirty() -
dispose
-
writeRoster
Store the roster in the default place, including making a backup if needed.Uses writeFile(String), a protected method that can write to a specific location.
-
reindex
Rebuild the Roster index and store it. -
reloadRosterFile
Update the in-memory Roster to be consistent with the current roster file. This removes any existing roster entries! -
setRosterIndexFileName
-
getRosterIndexFileName
-
getRosterIndexPath
-
getRosterFilesLocation
-
setRosterLocation
Set the default location for the Roster file, and all individual locomotive files.- Parameters:
f- Absolute pathname to use. A null or "" argument flags a return to the original default in the user's files directory. This parameter must be a potentially valid path on the system.
-
getRosterLocation
Absolute path to roster file location.Default is in the user's files directory, but can be set to anything.
- Returns:
- location of the Roster file
- See Also:
-
addPropertyChangeListener
Description copied from interface:PropertyChangeProviderAdd aPropertyChangeListenerto the listener list.- Specified by:
addPropertyChangeListenerin interfacePropertyChangeProvider- Specified by:
addPropertyChangeListenerin interfaceRosterGroupSelector- Parameters:
l- The PropertyChangeListener to be added
-
addPropertyChangeListener
Description copied from interface:PropertyChangeProviderAdd aPropertyChangeListenerfor a specific property.- Specified by:
addPropertyChangeListenerin interfacePropertyChangeProvider- Specified by:
addPropertyChangeListenerin interfaceRosterGroupSelector- Parameters:
propertyName- The name of the property to listen on.listener- The PropertyChangeListener to be added
-
firePropertyChange
-
removePropertyChangeListener
Description copied from interface:PropertyChangeProviderRemove the specified listener from this object.- Specified by:
removePropertyChangeListenerin interfacePropertyChangeProvider- Specified by:
removePropertyChangeListenerin interfaceRosterGroupSelector- Parameters:
l- ThePropertyChangeListenerto remove.
-
removePropertyChangeListener
Description copied from interface:PropertyChangeProviderRemove the specified listener of the specified property from this object.- Specified by:
removePropertyChangeListenerin interfacePropertyChangeProvider- Specified by:
removePropertyChangeListenerin interfaceRosterGroupSelector- Parameters:
propertyName- The name of the property to stop listening to.listener- ThePropertyChangeListenerto remove.
-
getPropertyChangeListeners
Description copied from interface:PropertyChangeProviderGet allPropertyChangeListeners currently attached to this object.- Specified by:
getPropertyChangeListenersin interfacePropertyChangeProvider- Returns:
- An array of PropertyChangeListeners.
-
getPropertyChangeListeners
Description copied from interface:PropertyChangeProviderGet allPropertyChangeListeners currently listening to changes to the specified property.- Specified by:
getPropertyChangeListenersin interfacePropertyChangeProvider- Parameters:
propertyName- the name of the property of interest- Returns:
- an array of PropertyChangeListeners
-
entryIdChanged
Notify that the ID of an entry has changed. This doesn't actually change the roster contents, but triggers a reordering of the roster contents.- Parameters:
r- the entry with a changed Id
-
getRosterGroupName
-
getRosterGroupProperty
Get the string for a RosterGroup property in a RosterEntry- Parameters:
name- The name of the rosterGroup- Returns:
- The full property string
-
addRosterGroup
Add a roster group, notifying all listeners of the change.This method fires the property change notification "RosterGroupAdded".
- Parameters:
rg- The group to be added
-
addRosterGroup
Add a roster group, notifying all listeners of the change.This method creates a
RosterGroup. UseaddRosterGroup(jmri.jmrit.roster.rostergroup.RosterGroup)if you need to add a subclass of RosterGroup. This method fires the property change notification "RosterGroupAdded".- Parameters:
rg- The name of the group to be added
-
addRosterGroups
Add a list ofRosterGroup. RosterGroups that are already known to the Roster are ignored.- Parameters:
groups- RosterGroups to add to the roster. RosterGroups already in the roster will not be added again.
-
removeRosterGroup
-
delRosterGroupList
Delete a roster group, notifying all listeners of the change.This method fires the property change notification ""RosterGroupRemoved"".
- Parameters:
rg- The group to be deleted
-
copyRosterGroupList
Copy a roster group, adding every entry in the roster group to the new group.If a roster group with the target name already exists, this method silently fails to rename the roster group. The GUI method CopyRosterGroupAction.performAction() catches this error and informs the user. This method fires the property change ""RosterGroupAdded"".
- Parameters:
oldName- Name of the roster group to be copiednewName- Name of the new roster group- See Also:
-
rosterGroupRenamed
-
renameRosterGroupList
Rename a roster group, while keeping every entry in the roster group.If a roster group with the target name already exists, this method silently fails to rename the roster group. The GUI method RenameRosterGroupAction.performAction() catches this error and informs the user. This method fires the property change ""RosterGroupRenamed"".
- Parameters:
oldName- Name of the roster group to be renamednewName- New name for the roster group- See Also:
-
getRosterGroupList
Get a list of the user defined roster group names.Strings are immutable, so deleting an item from the copy should not affect the system-wide list of roster groups.
- Returns:
- A list of the roster group names.
-
allEntries
Get the identifier for all entries in the roster.- Parameters:
locale- The desired locale- Returns:
- "All Entries" in the specified locale
-
getSelectedRosterGroup
Get the default roster group.This method ensures adherence to the RosterGroupSelector protocol
- Specified by:
getSelectedRosterGroupin interfaceRosterGroupSelector- Returns:
- The entire roster
-
getDefaultRosterGroup
- Returns:
- the defaultRosterGroup
-
setDefaultRosterGroup
- Parameters:
defaultRosterGroup- the defaultRosterGroup to set
-
getAllFileNames
Get an array of all the RosterEntry-containing files in the target directory.- Returns:
- a string array of file names for entries in this roster
-
getRosterGroups
Get the groups known to the roster itself. Note that changes to the returned Map will not be reflected in the Roster.- Returns:
- the rosterGroups
-
remapRosterGroup
Changes the key used to look up a RosterGroup by name. This is a helper method that does not fire a notification to any propertyChangeListeners.To rename a RosterGroup, use
RosterGroup.setName(java.lang.String).- Parameters:
group- The group being associated with newKey and will be disassociated with the key matchingRosterGroup.getName().newKey- The new key by which group can be found in the map of RosterGroups. This should match the intended new name of group.
-
propertyChange
- Specified by:
propertyChangein interfacePropertyChangeListener
-