001package jmri.managers; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.ArrayList; 006import java.util.HashMap; 007import java.util.Set; 008 009import jmri.Block; 010import jmri.BlockManager; 011import jmri.CabSignal; 012import jmri.CabSignalListListener; 013import jmri.CabSignalManager; 014import jmri.InstanceManager; 015import jmri.LocoAddress; 016 017/** 018 * Abstract implementation of the {@link jmri.CabSignalManager} interface. 019 * 020 * <hr> 021 * This file is part of JMRI. 022 * <p> 023 * JMRI is free software; you can redistribute it and/or modify it under the 024 * terms of version 2 of the GNU General Public License as published by the Free 025 * Software Foundation. See the "COPYING" file for a copy of this license. 026 * <p> 027 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 028 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 029 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 030 * 031 * @author Paul Bender Copyright (C) 2019 032 */ 033public abstract class AbstractCabSignalManager implements CabSignalManager, jmri.Disposable { 034 035 private final HashMap<LocoAddress, CabSignal> signalList; 036 private final ArrayList<CabSignalListListener> listListeners; 037 038 // keep a list of Blocks with listeners. 039 private final ArrayList<Block> _blocksWithListeners; 040 041 // the BlockManager and listeners this manager registers with; kept so 042 // that the listeners removed are the ones that were added 043 private final BlockManager blockManager; 044 private final PropertyChangeListener blockConfigListener = this::handleBlockConfigChanged; 045 private final PropertyChangeListener blockListener = this::handleBlockChange; 046 047 public AbstractCabSignalManager(){ 048 signalList = new HashMap<>(); 049 listListeners = new ArrayList<>(); 050 _blocksWithListeners = new ArrayList<>(); 051 blockManager = InstanceManager.getDefault(BlockManager.class); 052 blockManager.addPropertyChangeListener(BlockManager.PROPERTY_BEANS, blockConfigListener); 053 } 054 055 /** 056 * Find a CabSignal with the given address, and return it. If the CabSignal 057 * doesn't exit, create it. 058 * 059 * @param address the cab signal for the address 060 * @return an existing or new cab signal 061 */ 062 @Override 063 public CabSignal getCabSignal(LocoAddress address){ 064 if(_blocksWithListeners.isEmpty()) { 065 initBlocks(); 066 } 067 if(!signalList.containsKey(address)){ 068 signalList.put(address, createCabSignal(address)); 069 notifyCabSignalListChanged(); 070 } 071 return signalList.get(address); 072 } 073 074 /** 075 * Create a new cab signal with the given address. 076 * 077 * @param address the address the cab signal is for 078 * @return a new cab signal 079 */ 080 protected abstract CabSignal createCabSignal(LocoAddress address); 081 082 /** 083 * Remove an old CabSignal. 084 * 085 * @param address the address associated with the cab signal 086 */ 087 @Override 088 public void delCabSignal(LocoAddress address){ 089 if(signalList.containsKey(address)){ 090 signalList.remove(address); 091 notifyCabSignalListChanged(); 092 } 093 } 094 095 /** 096 * Get a list of known cab signal addresses. 097 * 098 * @return list of cab signal addresses 099 */ 100 @Override 101 public Set<LocoAddress> getCabSignalList(){ 102 return signalList.keySet(); 103 } 104 105 /** 106 * Get an array of known cab signals. 107 * 108 * @return array of cab signals 109 */ 110 @Override 111 public CabSignal[] getCabSignalArray(){ 112 return signalList.values().toArray(new CabSignal[1]); 113 } 114 115 /** 116 * Register a CabSignalListListener object with this CabSignalManager 117 * 118 * @param listener a CabSignal List Listener object. 119 */ 120 @Override 121 public void addCabSignalListListener(CabSignalListListener listener){ 122 if(!listListeners.contains(listener)){ 123 listListeners.add(listener); 124 } 125 } 126 127 /** 128 * Remove a CabSignalListListener object with this CabSignalManager 129 * 130 * @param listener a CabSignal List Listener object. 131 */ 132 @Override 133 public void removeCabSignalListListener(CabSignalListListener listener){ 134 if(listListeners.contains(listener)){ 135 listListeners.remove(listener); 136 } 137 } 138 139 /** 140 * Notify the registered CabSignalListListener objects that the CabSignalList 141 * has changed. 142 */ 143 @Override 144 public void notifyCabSignalListChanged(){ 145 for(CabSignalListListener l : listListeners){ 146 l.notifyCabSignalListChanged(); 147 } 148 } 149 150 // Adds changelistener to blocks 151 private void initBlocks(){ 152 Set<Block> blockSet = blockManager.getNamedBeanSet(); 153 for (Block b : blockSet) { 154 b.addPropertyChangeListener(blockListener); 155 _blocksWithListeners.add(b); 156 } 157 } 158 159 private void removeListenerFromBlocks(){ 160 for (Block b : _blocksWithListeners) { 161 b.removePropertyChangeListener(blockListener); 162 } 163 _blocksWithListeners.clear(); 164 } 165 166 /** 167 * Handle tasks when block contents change. 168 * @param e propChgEvent 169 */ 170 private void handleBlockChange(PropertyChangeEvent e) { 171 log.debug("property {} new value {} old value {}", 172 e.getPropertyName(), e.getNewValue(), e.getOldValue()); 173 if ( Block.PROPERTY_VALUE.equals(e.getPropertyName()) 174 && e.getOldValue() == null && e.getNewValue() != null ) { 175 for ( CabSignal c : signalList.values() ) { 176 if ( c.getBlock() == null ){ 177 c.setBlock(); // cause this cab signal to look for a block. 178 } 179 } 180 } 181 } 182 183 private void handleBlockConfigChanged(PropertyChangeEvent e) { 184 log.debug("blocks changed in blockmanager {}", e); 185 removeListenerFromBlocks(); 186 if ( !signalList.isEmpty() ) { // no need to add if no listeners. 187 initBlocks(); 188 } 189 } 190 191 @Override 192 public void dispose(){ 193 blockManager.removePropertyChangeListener(BlockManager.PROPERTY_BEANS, blockConfigListener); 194 for(CabSignal c : signalList.values()){ 195 c.dispose(); 196 } 197 removeListenerFromBlocks(); 198 } 199 200 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractCabSignalManager.class); 201 202}