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