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 */ 032abstract public class AbstractCabSignalManager implements CabSignalManager, jmri.Disposable { 033 034 protected HashMap<LocoAddress, CabSignal> signalList; 035 protected 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).addPropertyChangeListener("beans", this::handleBlockConfigChanged); 045 } 046 047 /** 048 * Find a CabSignal with the given address, and return it. If the CabSignal 049 * doesn't exit, create it. 050 * 051 * @param address the cab signal for the address 052 * @return an existing or new cab signal 053 */ 054 @Override 055 public CabSignal getCabSignal(LocoAddress address){ 056 if(_blocksWithListeners.isEmpty()) { 057 initBlocks(); 058 } 059 if(!signalList.containsKey(address)){ 060 signalList.put(address, createCabSignal(address)); 061 notifyCabSignalListChanged(); 062 } 063 return signalList.get(address); 064 } 065 066 /** 067 * Create a new cab signal with the given address. 068 * 069 * @param address the address the cab signal is for 070 * @return a new cab signal 071 */ 072 abstract protected CabSignal createCabSignal(LocoAddress address); 073 074 /** 075 * Remove an old CabSignal. 076 * 077 * @param address the address associated with the cab signal 078 */ 079 @Override 080 public void delCabSignal(LocoAddress address){ 081 if(signalList.containsKey(address)){ 082 signalList.remove(address); 083 notifyCabSignalListChanged(); 084 } 085 } 086 087 /** 088 * Get a list of known cab signal addresses. 089 * 090 * @return list of cab signal addresses 091 */ 092 @Override 093 public Set<LocoAddress> getCabSignalList(){ 094 return signalList.keySet(); 095 } 096 097 /** 098 * Get an array of known cab signals. 099 * 100 * @return array of cab signals 101 */ 102 @Override 103 public CabSignal[] getCabSignalArray(){ 104 return signalList.values().toArray(new CabSignal[1]); 105 } 106 107 /** 108 * Register a CabSignalListListener object with this CabSignalManager 109 * 110 * @param listener a CabSignal List Listener object. 111 */ 112 @Override 113 public void addCabSignalListListener(CabSignalListListener listener){ 114 if(!listListeners.contains(listener)){ 115 listListeners.add(listener); 116 } 117 } 118 119 /** 120 * Remove a CabSignalListListener object with this CabSignalManager 121 * 122 * @param listener a CabSignal List Listener object. 123 */ 124 @Override 125 public void removeCabSignalListListener(CabSignalListListener listener){ 126 if(listListeners.contains(listener)){ 127 listListeners.remove(listener); 128 } 129 } 130 131 /** 132 * Notify the registered CabSignalListListener objects that the CabSignalList 133 * has changed. 134 */ 135 @Override 136 public void notifyCabSignalListChanged(){ 137 for(CabSignalListListener l : listListeners){ 138 l.notifyCabSignalListChanged(); 139 } 140 } 141 142 // Adds changelistener to blocks 143 private void initBlocks(){ 144 Set<Block> blockSet = InstanceManager.getDefault(BlockManager.class).getNamedBeanSet(); 145 for (Block b : blockSet) { 146 b.addPropertyChangeListener(this::handleBlockChange); 147 _blocksWithListeners.add(b); 148 } 149 } 150 151 private void removeListenerFromBlocks(){ 152 for (Block b : _blocksWithListeners) { 153 b.removePropertyChangeListener(this::handleBlockChange); 154 } 155 _blocksWithListeners.clear(); 156 } 157 158 /** 159 * Handle tasks when block contents change. 160 * @param e propChgEvent 161 */ 162 private void handleBlockChange(PropertyChangeEvent e) { 163 log.debug("property {} new value {} old value {}",e.getPropertyName(), e.getNewValue(), e.getOldValue()); 164 if (e.getPropertyName().equals("value")){ 165 if(e.getOldValue() == null && e.getNewValue() != null){ 166 for(CabSignal c : signalList.values()){ 167 if(c.getBlock() == null){ 168 c.setBlock(); // cause this cab signal to look for a block. 169 } 170 } 171 } 172 } 173 } 174 175 private void handleBlockConfigChanged(PropertyChangeEvent e) { 176 log.debug("blocks changed in blockmanager {}", e); 177 removeListenerFromBlocks(); 178 if ( !signalList.isEmpty() ) { // no need to add if no listeners. 179 initBlocks(); 180 } 181 } 182 183 @Override 184 public void dispose(){ 185 InstanceManager.getDefault(BlockManager.class).removePropertyChangeListener("beans", this::handleBlockConfigChanged); 186 for(CabSignal c : signalList.values()){ 187 c.dispose(); 188 } 189 removeListenerFromBlocks(); 190 } 191 192 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractCabSignalManager.class); 193 194}