001package jmri.jmrit.ctc;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.List;
006import jmri.Block;
007import jmri.ConfigureManager;
008import jmri.InstanceManager;
009import jmri.InstanceManagerAutoDefault;
010import jmri.Manager;
011import jmri.NamedBean;
012import jmri.NamedBeanHandle;
013import jmri.NamedBeanHandleManager;
014import jmri.NamedBeanUsageReport;
015import jmri.jmrit.ctc.ctcserialdata.*;
016import jmri.jmrit.ctc.editor.code.*;
017
018/**
019 * Start the CtcManager and register with the instance and configuration managers.
020 * <ul>
021 *   <li>Create/provide the ProgramProperties instance</li>
022 *   <li>Create/provide the CTCSerialData instance</li>
023 *   <li>Provide the OtherData instance</li>
024 *   <li>Provide hash maps of beans used by CTC</li>
025 *   <li>Veto deletes for beans used by CTC</li>
026 * </ul>
027 *
028 * @author Dave Sand Copyright (C) 2020
029 */
030public class CtcManager implements InstanceManagerAutoDefault, java.beans.VetoableChangeListener {
031
032    ProgramProperties programProperties = null;
033    CTCSerialData ctcSerialData = null;
034    HashMap<String, NBHSensor> nbhSensors = new HashMap<>();
035    HashMap<String, NBHSignal> nbhSignals = new HashMap<>();
036    HashMap<String, NBHTurnout> nbhTurnouts = new HashMap<>();
037    HashMap<String, NamedBeanHandle<Block>> blocks = new HashMap<>();
038
039    // Search results
040    NBHSensor  foundSensor;
041    NBHSignal  foundSignal;
042    NBHTurnout foundTurnout;
043    Block      foundBlock;
044
045    List<NamedBeanUsageReport> usageReport;
046
047    public CtcManager() {
048        InstanceManager.setDefault(CtcManager.class, this);
049        InstanceManager.getOptionalDefault(ConfigureManager.class).ifPresent(cm -> {
050            cm.registerConfig(this, getXMLOrder());
051        });
052        InstanceManager.getDefault(jmri.SensorManager.class).addVetoableChangeListener(this);
053        InstanceManager.getDefault(jmri.SignalHeadManager.class).addVetoableChangeListener(this);
054        InstanceManager.getDefault(jmri.SignalMastManager.class).addVetoableChangeListener(this);
055        InstanceManager.getDefault(jmri.TurnoutManager.class).addVetoableChangeListener(this);
056        InstanceManager.getDefault(jmri.BlockManager.class).addVetoableChangeListener(this);
057        log.debug("CtcManager started");  // NOI18N
058    }
059
060    public ProgramProperties getProgramProperties() {
061        if (programProperties == null) {
062            programProperties = new ProgramProperties();
063        }
064        return programProperties;
065    }
066
067    public ProgramProperties newProgramProperties() {
068        programProperties = new ProgramProperties();
069        return programProperties;
070    }
071
072    public CTCSerialData getCTCSerialData() {
073        if (ctcSerialData == null) {
074            ctcSerialData = new CTCSerialData();
075        }
076        return ctcSerialData;
077    }
078
079    public CTCSerialData newCTCSerialData() {
080        ctcSerialData = new CTCSerialData();
081
082        nbhSensors.clear();
083        nbhSignals.clear();
084        nbhTurnouts.clear();
085        blocks.clear();
086
087        return ctcSerialData;
088    }
089
090    public OtherData getOtherData() {
091        if (ctcSerialData == null) {
092            ctcSerialData = getCTCSerialData();
093        }
094        return ctcSerialData.getOtherData();
095    }
096
097    public NBHSensor getNBHSensor(String name) {
098        // check for new names
099        return nbhSensors.get(name);
100    }
101
102    public void putNBHSensor(String name, NBHSensor nbh) {
103        NBHSensor oldSensor = nbhSensors.put(name, nbh);
104        log.debug("sensor put = {} -- {}", name, nbh);  // NOI18N
105        if (oldSensor != null) {
106            log.debug("---- duplicate sensor: {} -- {}", name, nbh);  // NOI18N
107        }
108    }
109
110    public NBHSignal getNBHSignal(String name) {
111        // check for new names
112        return nbhSignals.get(name);
113    }
114
115    public void putNBHSignal(String name, NBHSignal nbh) {
116        NBHSignal oldSignal = nbhSignals.put(name, nbh);
117        log.debug("signal put = {} -- {}", name, nbh);  // NOI18N
118        if (oldSignal != null) {
119            log.debug("---- duplicate signal: {} -- {}", name, nbh);  // NOI18N
120        }
121    }
122
123    public NBHTurnout getNBHTurnout(String name) {
124        // check for new names
125        return nbhTurnouts.get(name);
126    }
127
128    public void putNBHTurnout(String name, NBHTurnout nbh) {
129        NBHTurnout oldTurnout = nbhTurnouts.put(name, nbh);
130        log.debug("turnout put = {} -- {}", name, nbh);  // NOI18N
131        if (oldTurnout != null) {
132            log.debug("---- duplicate turnout: {} -- {}", name, nbh);  // NOI18N
133        }
134    }
135
136    public NamedBeanHandle<Block> getBlock(String name) {
137        // check for new names
138        return blocks.get(name);
139    }
140
141    public void putBlock(String name, NamedBeanHandle<Block> block) {
142        NamedBeanHandle<Block> oldBlock = blocks.put(name, block);
143        log.debug("block put = {} -- {}", name, block);  // NOI18N
144        if (oldBlock != null) {
145            log.debug("---- duplicate block: {} -- {}", name, block);  // NOI18N
146        }
147    }
148
149    public int getXMLOrder() {
150        return Manager.CTCDATA;
151    }
152
153    public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException {
154        jmri.NamedBean nb = (jmri.NamedBean) evt.getOldValue();
155        if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N
156            if (findNBHforBean(nb)) {
157                java.beans.PropertyChangeEvent e = new java.beans.PropertyChangeEvent(this, "DoNotDelete", null, null);  // NOI18N
158                throw new java.beans.PropertyVetoException(getVetoDetails(nb), e);
159            }
160        }
161    }
162
163    String getVetoDetails(NamedBean nb) {
164        StringBuilder sb = new StringBuilder();
165        sb.append(Bundle.getMessage("CtcManagerDeleteVetoed", nb.getBeanType()));  // NOI18N
166        for (NamedBeanUsageReport report : getUsageReport(nb)) {
167            sb.append(Bundle.getMessage("VetoDetailLine", report.usageData));    // NOI18N
168        }
169        return sb.toString();
170    }
171
172    boolean findNBHforBean(NamedBean nb) {
173        if (nb == null) return false;
174        boolean found = false;
175        foundSensor = null;
176        foundSignal = null;
177        foundTurnout = null;
178        foundBlock = null;
179
180        if (nb instanceof jmri.Sensor) {
181            for (NBHSensor sensor : nbhSensors.values()) {
182                if (nb.equals(sensor.getBean())) {
183                    foundSensor = sensor;
184                    found = true;
185                    break;
186                }
187            }
188        }
189
190        if (nb instanceof jmri.SignalHead || nb instanceof jmri.SignalMast) {
191            for (NBHSignal signal : nbhSignals.values()) {
192                if (nb.equals(signal.getBean())) {
193                    foundSignal = signal;
194                    found = true;
195                    break;
196                }
197            }
198        }
199
200        if (nb instanceof jmri.Turnout) {
201            for (NBHTurnout turnout : nbhTurnouts.values()) {
202                if (nb.equals(turnout.getBean())) {
203                    foundTurnout = turnout;
204                    found = true;
205                    break;
206                }
207            }
208        }
209
210        if (nb instanceof Block) {
211            for (NamedBeanHandle<Block> block : blocks.values()) {
212                if (nb.equals(block.getBean())) {
213                    foundBlock = block.getBean();
214                    found = true;
215                    break;
216                }
217            }
218        }
219        return found;
220    }
221
222    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
223        usageReport = new ArrayList<>();
224        if (findNBHforBean(bean)) {
225            // Other data
226            if (getOtherData()._mFleetingToggleInternalSensor.equals(foundSensor) ||
227                    getOtherData()._mCTCDebugSystemReloadInternalSensor.equals(foundSensor) ||
228                    getOtherData()._mCTCDebug_TrafficLockingRuleTriggeredDisplayInternalSensor.equals(foundSensor)) {
229                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedOther", Bundle.getMessage("WhereUsedOther")));  // NOI18N
230            }
231
232            // O.S. Sections
233            getCTCSerialData().getCodeButtonHandlerDataArrayList().forEach(cbhd -> {
234                getCodeButtonHandleDataUsage(cbhd);
235            });
236        }
237        return usageReport;
238    }
239
240    void getCodeButtonHandleDataUsage(CodeButtonHandlerData cbhd) {
241        String osName = cbhd.myShortStringNoComma();
242
243        // CB Sensors
244        if (cbhd._mCodeButtonInternalSensor.equals(foundSensor) ||
245                cbhd._mOSSectionOccupiedExternalSensor.equals(foundSensor) ||
246                cbhd._mOSSectionOccupiedExternalSensor2.equals(foundSensor)) {
247            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "CB")));  // NOI18N
248        }
249
250        // SIDI Sensors
251        if (cbhd._mSIDI_LeftInternalSensor.equals(foundSensor) ||
252                cbhd._mSIDI_NormalInternalSensor.equals(foundSensor) ||
253                cbhd._mSIDI_RightInternalSensor.equals(foundSensor)) {
254            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "SIDI")));  // NOI18N
255        }
256
257        // SIDI Signals
258        cbhd._mSIDI_LeftRightTrafficSignals.forEach(signal -> {
259            if (signal.equals(foundSignal)) {
260                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSignal", osName, "SIDI")));  // NOI18N
261            }
262        });
263        cbhd._mSIDI_RightLeftTrafficSignals.forEach(signal -> {
264            if (signal.equals(foundSignal)) {
265                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSignal", osName, "SIDI")));  // NOI18N
266            }
267        });
268
269        // SIDL Sensors
270        if (cbhd._mSIDL_LeftInternalSensor.equals(foundSensor) ||
271                cbhd._mSIDL_NormalInternalSensor.equals(foundSensor) ||
272                cbhd._mSIDL_RightInternalSensor.equals(foundSensor)) {
273            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "SIDL")));  // NOI18N
274        }
275
276        // SWDI Sensors
277        if (cbhd._mSWDI_NormalInternalSensor.equals(foundSensor) ||
278                cbhd._mSWDI_ReversedInternalSensor.equals(foundSensor)) {
279            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "SWDI")));  // NOI18N
280        }
281
282        // SWDI Turnout
283        if (cbhd._mSWDI_ExternalTurnout.equals(foundTurnout)) {
284            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedTurnout", osName, "SWDI")));  // NOI18N
285        }
286
287        // SWDL Sensor
288        if (cbhd._mSWDL_InternalSensor.equals(foundSensor)) {
289            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "SWDL")));  // NOI18N
290        }
291
292        callOnDataUsage(cbhd, osName);
293        traffficLockingDataUsage(cbhd, osName);
294
295        // TUL Sensors
296        if (cbhd._mTUL_DispatcherInternalSensorLockToggle.equals(foundSensor) ||
297                cbhd._mTUL_DispatcherInternalSensorUnlockedIndicator.equals(foundSensor)) {
298            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "TUL")));  // NOI18N
299        }
300
301        // TUL Turnouts
302        if (cbhd._mTUL_ExternalTurnout.equals(foundTurnout) ||
303                cbhd._mTUL_AdditionalExternalTurnout1.equals(foundTurnout) ||
304                cbhd._mTUL_AdditionalExternalTurnout2.equals(foundTurnout) ||
305                cbhd._mTUL_AdditionalExternalTurnout3.equals(foundTurnout)) {
306            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedTurnout", osName, "TUL")));  // NOI18N
307        }
308
309        // IL Signals
310        cbhd._mIL_Signals.forEach(signal -> {
311            if (signal.equals(foundSignal)) {
312                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSignal", osName, "IL")));  // NOI18N
313            }
314        });
315    }
316
317    void callOnDataUsage(CodeButtonHandlerData cbhd, String osName) {
318        // CO Sensor
319        if (cbhd._mCO_CallOnToggleInternalSensor.equals(foundSensor)) {
320            usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "CO")));  // NOI18N
321        }
322        cbhd._mCO_GroupingsList.forEach(row -> {
323            // Sensor
324            if (row._mCalledOnExternalSensor.equals(foundSensor)) {
325                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "CO")));  // NOI18N
326            }
327
328            // Signal
329            if (row._mExternalSignal.equals(foundSignal)) {
330                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSignal", osName, "CO")));  // NOI18N
331            }
332
333            // Block
334            if (row._mExternalBlock != null && row._mExternalBlock.getBean().equals(foundBlock)) {
335                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedBlock", osName, "CO")));  // NOI18N
336            }
337
338            // Switch indicator sensors
339            row._mSwitchIndicators.forEach(sw -> {
340                if (sw.equals(foundSensor)) {
341                    usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "CO")));  // NOI18N
342                }
343            });
344        });
345    }
346
347    void traffficLockingDataUsage(CodeButtonHandlerData cbhd, String osName) {
348        cbhd._mTRL_LeftTrafficLockingRules.forEach(rule -> {
349            traffficLockingRuleDataUsage(rule, osName);
350        });
351
352        cbhd._mTRL_RightTrafficLockingRules.forEach(rule -> {
353            traffficLockingRuleDataUsage(rule, osName);
354        });
355    }
356
357    void traffficLockingRuleDataUsage(TrafficLockingData rule, String osName) {
358        // Signal -- _mDestinationSignalOrComment
359        if (foundSignal != null) {
360            if (rule._mDestinationSignalOrComment.equals(foundSignal.getHandleName())) {
361                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSignal", osName, "TRL")));  // NOI18N
362            }
363        }
364
365        // Occupancy sensors
366        for (NBHSensor sensor : rule._mOccupancyExternalSensors) {
367            if (sensor.equals(foundSensor)) {
368                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "TRL")));  // NOI18N
369                break;
370            }
371        }
372
373        // Optional sensors
374        for (NBHSensor sensor : rule._mOptionalExternalSensors) {
375            if (sensor.equals(foundSensor)) {
376                usageReport.add(new NamedBeanUsageReport("CtcWhereUsedCBHD", Bundle.getMessage("WhereUsedSensor", osName, "TRL")));  // NOI18N
377                break;
378            }
379        }
380    }
381
382    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CtcManager.class);
383}