001package jmri.jmrit.blockboss.configurexml;
002
003import jmri.InstanceManager;
004import jmri.Manager;
005import jmri.SignalHeadManager;
006import jmri.jmrit.blockboss.BlockBossLogic;
007import jmri.jmrit.blockboss.BlockBossLogicProvider;
008import org.jdom2.DataConversionException;
009import org.jdom2.Element;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import java.util.Collections;
014import java.util.Enumeration;
015import java.util.List;
016
017/**
018 * Handle XML persistance of Simple Signal Logic objects.
019 *
020 * <p>
021 * In JMRI 2.1.5, the XML written by this package was changed.
022 * <p>
023 * Previously, it wrote a single "blocks" element, which contained multiple
024 * "block" elements to represent each individual BlockBoss (Simple Signal Logic)
025 * object.
026 * <p>
027 * These names were too generic, and conflicted with storing true Block objects.
028 * <p>
029 * Starting in JMRI 2.1.5 (May 2008), these were changed to "signalelements" and
030 * "signalelement" respectively.
031 *
032 * @author Bob Jacobsen Copyright: Copyright (c) 2003, 2005
033 *
034 * Revisions to add facing point sensors, approach lighting, and limited speed.
035 * Dick Bronson (RJB) 2006
036 */
037public class BlockBossLogicProviderXml extends jmri.configurexml.AbstractXmlAdapter {
038
039    private static final String SYSTEM_NAME = "systemName";
040    private static final String SIGNAL = "signal";
041    private static final String APPROACHSENSOR_1 = "approachsensor1";
042    private static final String WATCHEDTURNOUT = "watchedturnout";
043    private static final String WATCHEDSIGNAL_1 = "watchedsignal1";
044    private static final String WATCHEDSIGNAL_1_ALT = "watchedsignal1alt";
045    private static final String WATCHEDSIGNAL_2 = "watchedsignal2";
046    private static final String WATCHEDSIGNAL_2_ALT = "watchedsignal2alt";
047    private static final String WATCHEDSENSOR_1 = "watchedsensor1";
048    private static final String WATCHEDSENSOR_1_ALT = "watchedsensor1alt";
049    private static final String WATCHEDSENSOR_2 = "watchedsensor2";
050    private static final String WATCHEDSENSOR_2_ALT = "watchedsensor2alt";
051    private static final String LIMITSPEED_1 = "limitspeed1";
052    private static final String LIMITSPEED_2 = "limitspeed2";
053    private static final String RESTRICTINGSPEED_1 = "restrictingspeed1";
054    private static final String RESTRICTINGSPEED_2 = "restrictingspeed2";
055    private static final String USEFLASHYELLOW = "useflashyellow";
056    private static final String DISTANTSIGNAL = "distantsignal";
057    private final BlockBossLogicProvider blockBossLogicProvider;
058
059    public BlockBossLogicProviderXml() {
060        blockBossLogicProvider = InstanceManager.getDefault(BlockBossLogicProvider.class);
061    }
062
063    /**
064     * Default implementation for storing the contents of all the BLockBossLogic
065     * elements.
066     * <p>
067     * Static members in the BlockBossLogic class record the complete set of
068     * items. This function writes those out as a single XML element.
069     *
070     * @param o Object to start process, but not actually used
071     * @return Element containing the complete info
072     */
073    @Override
074    public Element store(Object o) {
075
076        Enumeration<BlockBossLogic> e = Collections.enumeration(blockBossLogicProvider.provideAll());
077        if (!e.hasMoreElements()) {
078            return null;  // nothing to write!
079        }
080        Element blocks = new Element("signalelements");
081        blocks.setAttribute("class", this.getClass().getName());
082
083        while (e.hasMoreElements()) {
084            BlockBossLogic p = e.nextElement();
085            Element block = getElementFromBlockBossLogic(p);
086            blocks.addContent(block);
087        }
088
089        return blocks;
090    }
091
092    private Element getElementFromBlockBossLogic(BlockBossLogic p) {
093        Element block = new Element("signalelement");
094        block.setAttribute(SIGNAL, p.getDrivenSignal());
095        block.setAttribute("mode", "" + p.getMode());
096
097        if (p.getApproachSensor1() != null) {
098            block.setAttribute(APPROACHSENSOR_1, p.getApproachSensor1());
099        }
100
101        if (p.getSensor1() != null) {
102            block.addContent(storeSensor(p.getSensor1()));
103        }
104        if (p.getSensor2() != null) {
105            block.addContent(storeSensor(p.getSensor2()));
106        }
107        if (p.getSensor3() != null) {
108            block.addContent(storeSensor(p.getSensor3()));
109        }
110        if (p.getSensor4() != null) {
111            block.addContent(storeSensor(p.getSensor4()));
112        }
113        if (p.getSensor5() != null) {
114            block.addContent(storeSensor(p.getSensor5()));
115        }
116
117        if (p.getTurnout() != null) {
118            block.setAttribute(WATCHEDTURNOUT, p.getTurnout());
119        }
120        if (p.getWatchedSignal1() != null) {
121            block.setAttribute(WATCHEDSIGNAL_1, p.getWatchedSignal1());
122        }
123        if (p.getWatchedSignal1Alt() != null) {
124            block.setAttribute(WATCHEDSIGNAL_1_ALT, p.getWatchedSignal1Alt());
125        }
126        if (p.getWatchedSignal2() != null) {
127            block.setAttribute(WATCHEDSIGNAL_2, p.getWatchedSignal2());
128        }
129        if (p.getWatchedSignal2Alt() != null) {
130            block.setAttribute(WATCHEDSIGNAL_2_ALT, p.getWatchedSignal2Alt());
131        }
132        if (p.getWatchedSensor1() != null) {
133            block.setAttribute(WATCHEDSENSOR_1, p.getWatchedSensor1());
134        }
135        if (p.getWatchedSensor1Alt() != null) {
136            block.setAttribute(WATCHEDSENSOR_1_ALT, p.getWatchedSensor1Alt());
137        }
138        if (p.getWatchedSensor2() != null) {
139            block.setAttribute(WATCHEDSENSOR_2, p.getWatchedSensor2());
140        }
141        if (p.getWatchedSensor2Alt() != null) {
142            block.setAttribute(WATCHEDSENSOR_2_ALT, p.getWatchedSensor2Alt());
143        }
144
145        block.setAttribute(LIMITSPEED_1, "" + p.getLimitSpeed1());
146        block.setAttribute(LIMITSPEED_2, "" + p.getLimitSpeed2());
147        if (p.getRestrictingSpeed1())
148            block.setAttribute(RESTRICTINGSPEED_1, "" + p.getRestrictingSpeed1());
149        if (p.getRestrictingSpeed2())
150            block.setAttribute(RESTRICTINGSPEED_2, "" + p.getRestrictingSpeed2());
151        block.setAttribute(USEFLASHYELLOW, "" + p.getUseFlash());
152        block.setAttribute(DISTANTSIGNAL, "" + p.getDistantSignal());
153
154        // add comment, if present
155        if (p.getComment() != null) {
156            Element c = new Element("comment");
157            c.addContent(p.getComment());
158            block.addContent(c);
159        }
160        return block;
161    }
162
163    private Element storeSensor(String name) {
164        Element e = new Element("sensorname");
165        e.addContent(name);
166        return e;
167    }
168
169    @Override
170    public boolean load(Element shared, Element perNode) {
171        List<Element> l = shared.getChildren("signalelement");
172
173        // try old format if there are no new entries
174        // this is for backward compatibility only
175        if (l.size() == 0) {
176            l = shared.getChildren("block");
177        }
178
179        // process each item
180        for (Element block : l) {
181            BlockBossLogic bb = getBlockBossLogicFromElement(block);
182            if (bb == null) {
183                continue;
184            }
185            loadBlockBossLogicDetailsFromElement(block, bb);
186        }
187        return true;
188    }
189
190    private boolean loadBlockBossLogicDetailsFromElement(Element block, BlockBossLogic bb) {
191        boolean result = loadOptionalApproachSensor(block, bb);
192
193        result &= loadOptionalWatchedSensor(block, bb);
194
195        result &= loadOldFormSensorNames(block, bb);
196
197        try {
198            bb.setMode(block.getAttribute("mode").getIntValue());
199            if (block.getAttribute(DISTANTSIGNAL) != null) {
200                bb.setDistantSignal(block.getAttribute(DISTANTSIGNAL).getBooleanValue());
201            }
202            if (block.getAttribute(LIMITSPEED_1) != null) {
203                bb.setLimitSpeed1(block.getAttribute(LIMITSPEED_1).getBooleanValue());
204            }
205            if (block.getAttribute(RESTRICTINGSPEED_1) != null) {
206                bb.setRestrictingSpeed1(block.getAttribute(RESTRICTINGSPEED_1).getBooleanValue());
207            }
208            if (block.getAttribute(LIMITSPEED_2) != null) {
209                bb.setLimitSpeed2(block.getAttribute(LIMITSPEED_2).getBooleanValue());
210            }
211            if (block.getAttribute(RESTRICTINGSPEED_2) != null) {
212                bb.setRestrictingSpeed2(block.getAttribute(RESTRICTINGSPEED_2).getBooleanValue());
213            }
214            result &= loadWatchedTurnout(block, bb);
215
216            result &= loadWatchedSignal1(block, bb);
217
218            result &= loadWAtchedSignal1Alt(block, bb);
219
220            result &= loadWatchedSignal2(block, bb);
221
222            result &= loadWatchedSignal2Alt(block, bb);
223
224            result &= loadWatchedSensor1(block, bb);
225
226            result &= loadWatchedSensor1Alt(block, bb);
227
228            result &= loadWatchedSensor2(block, bb);
229
230            result &= loadWatchedSensor2Alt(block, bb);
231
232            // load comment, if present
233            String c = block.getChildText("comment");
234            if (c != null) {
235                bb.setComment(c);
236            }
237
238        } catch (org.jdom2.DataConversionException e) {
239            log.warn("error reading blocks from file", e);
240            result = false;
241        } catch (IllegalArgumentException e) {
242            log.error("An error occurred in the signal element attribute list");
243            result = false;
244        }
245        try {
246            blockBossLogicProvider.register(bb);
247            bb.start();
248        } catch (IllegalArgumentException e) {
249            log.debug("An error occurred trying to start the signal logic {} :: message = {}", bb.getDrivenSignal(), e.getMessage());
250            result = false;
251        }
252        return result;
253    }
254
255    private boolean loadOldFormSensorNames(Element block, BlockBossLogic bb) {
256        boolean result = true;
257        // old form of sensors with system names
258        List<Element> sl = block.getChildren("sensor");
259        try {
260            getSensorAttributesUsingSystemName(bb, sl);
261        } catch (IllegalArgumentException e) {
262            log.error("An error occurred loading the sensors list in the SSL");
263            result = false;
264        }
265        // new form of sensors with system names
266        sl = block.getChildren("sensorname");
267        try {
268            if (sl.size() >= 1 && sl.get(0) != null) {
269                bb.setSensor1(sl.get(0).getText());
270            }
271        } catch (IllegalArgumentException e) {
272            log.error("An error occurred loading the sensor1 list in the SSL for {}", bb.getDrivenSignal());
273            result = false;
274        }
275
276        try {
277            if (sl.size() >= 2 && sl.get(1) != null) {
278                bb.setSensor2(sl.get(1).getText());
279            }
280        } catch (IllegalArgumentException e) {
281            log.error("An error occurred loading the sensor2 list in the SSL for {}", bb.getDrivenSignal());
282            result = false;
283        }
284
285        try {
286            if (sl.size() >= 3 && sl.get(2) != null) {
287                bb.setSensor3(sl.get(2).getText());
288            }
289        } catch (IllegalArgumentException e) {
290            log.error("An error occurred loading the sensor3 list in the SSL for {}", bb.getDrivenSignal());
291            result = false;
292        }
293
294        try {
295            if (sl.size() >= 4 && sl.get(3) != null) {
296                bb.setSensor4(sl.get(3).getText());
297            }
298        } catch (IllegalArgumentException e) {
299            log.error("An error occurred loading the sensor4 list in the SSL for {}", bb.getDrivenSignal());
300            result = false;
301        }
302
303        try {
304            if (sl.size() >= 5 && sl.get(4) != null) {
305                bb.setSensor5(sl.get(4).getText());
306            }
307        } catch (IllegalArgumentException e) {
308            log.error("An error occurred loading the sensor5 list in the SSL for {}", bb.getDrivenSignal());
309            result = false;
310        }
311        return result;
312    }
313
314    private boolean loadOptionalWatchedSensor(Element block, BlockBossLogic bb) {
315        boolean result = true;
316        if (block.getAttribute("watchedsensor") != null) {   // for older XML files
317            try {
318                bb.setSensor1(block.getAttributeValue("watchedsensor"));
319            } catch (IllegalArgumentException e) {
320                log.error("An error occurred loading the watched sensor in the SSL for {}", bb.getDrivenSignal());
321                result = false;
322            }
323        }
324        return result;
325    }
326
327    private boolean loadOptionalApproachSensor(Element block, BlockBossLogic bb) {
328        boolean result = true;
329        if (block.getAttribute(APPROACHSENSOR_1) != null) {
330            try {
331                bb.setApproachSensor1(block.getAttributeValue(APPROACHSENSOR_1));
332            } catch (IllegalArgumentException e) {
333                log.error("An error occurred loading the approach sensor for the signal elements for {}", bb.getDrivenSignal());
334                result = false;
335            }
336        }
337        return result;
338    }
339
340    private boolean loadWatchedSensor2Alt(Element block, BlockBossLogic bb) {
341        boolean result = true;
342        try {
343            if (block.getAttribute(WATCHEDSENSOR_2_ALT) != null) {
344                bb.setWatchedSensor2Alt(block.getAttributeValue(WATCHEDSENSOR_2_ALT));
345            }
346        } catch (IllegalArgumentException e) {
347            log.error("An error occurred in retrieving the watched sensor 2 alt ({})element attribute list for {}", block.getAttributeValue(WATCHEDSENSOR_2_ALT), bb.getDrivenSignal());
348            result = false;
349        }
350        return result;
351    }
352
353    private boolean loadWatchedSensor2(Element block, BlockBossLogic bb) {
354        boolean result = true;
355        try {
356            if (block.getAttribute(WATCHEDSENSOR_2) != null) {
357                bb.setWatchedSensor2(block.getAttributeValue(WATCHEDSENSOR_2));
358            }
359        } catch (IllegalArgumentException e) {
360            log.error("An error occurred in retrieving the watched sensor 2 ({}) element attribute list for {}", block.getAttributeValue(WATCHEDSENSOR_2), bb.getDrivenSignal());
361            result = false;
362        }
363        return result;
364    }
365
366    private boolean loadWatchedSensor1Alt(Element block, BlockBossLogic bb) {
367        boolean result = true;
368        try {
369            if (block.getAttribute(WATCHEDSENSOR_1_ALT) != null) {
370                bb.setWatchedSensor1Alt(block.getAttributeValue(WATCHEDSENSOR_1_ALT));
371            }
372        } catch (IllegalArgumentException e) {
373            log.error("An error occurred in retrieving the watched sensor 1 alt ({}) element attribute list for {}", block.getAttributeValue(WATCHEDSENSOR_1_ALT), bb.getDrivenSignal());
374            result = false;
375        }
376        return result;
377    }
378
379    private boolean loadWatchedSensor1(Element block, BlockBossLogic bb) {
380        boolean result = true;
381        try {
382            if (block.getAttribute(WATCHEDSENSOR_1) != null) {
383                bb.setWatchedSensor1(block.getAttributeValue(WATCHEDSENSOR_1));
384            }
385        } catch (IllegalArgumentException e) {
386            log.error("An error occurred in retrieving the watched sensor 1 ({}) element attribute list for {}", block.getAttributeValue(WATCHEDSENSOR_1), bb.getDrivenSignal());
387            result = false;
388        }
389        return result;
390    }
391
392    private boolean loadWatchedSignal2Alt(Element block, BlockBossLogic bb) {
393        boolean result = true;
394        try {
395            if (block.getAttribute(WATCHEDSIGNAL_2_ALT) != null) {
396                bb.setWatchedSignal2Alt(block.getAttributeValue(WATCHEDSIGNAL_2_ALT));
397            }
398        } catch (IllegalArgumentException e) {
399            log.error("An error occurred in retrieving the watched signal 2 alt ({}) element attribute list for {}", block.getAttributeValue(WATCHEDSIGNAL_2_ALT), bb.getDrivenSignal());
400            result = false;
401        }
402        return result;
403    }
404
405    private boolean loadWatchedSignal2(Element block, BlockBossLogic bb) {
406        boolean result = true;
407        try {
408            if (block.getAttribute(WATCHEDSIGNAL_2) != null) {
409                bb.setWatchedSignal2(block.getAttributeValue(WATCHEDSIGNAL_2));
410            }
411
412        } catch (IllegalArgumentException e) {
413            log.error("An error occurred in retrieving the watched signal 2 ({})element attribute list for {}", block.getAttributeValue(WATCHEDSIGNAL_2), bb.getDrivenSignal());
414            result = false;
415        }
416        return result;
417    }
418
419    private boolean loadWAtchedSignal1Alt(Element block, BlockBossLogic bb) {
420        boolean result = true;
421        try {
422            if (block.getAttribute(WATCHEDSIGNAL_1_ALT) != null) {
423                bb.setWatchedSignal1Alt(block.getAttributeValue(WATCHEDSIGNAL_1_ALT));
424            }
425        } catch (IllegalArgumentException e) {
426            log.error("An error occurred in retrieving the watched signal 1 alt ({})element attribute list for {}", block.getAttributeValue(WATCHEDSIGNAL_1_ALT), bb.getDrivenSignal());
427            result = false;
428        }
429        return result;
430    }
431
432    private boolean loadWatchedSignal1(Element block, BlockBossLogic bb) throws DataConversionException {
433        boolean result = true;
434        try {
435            if (block.getAttribute(WATCHEDSIGNAL_1) != null) {
436                bb.setWatchedSignal1(block.getAttributeValue(WATCHEDSIGNAL_1), block.getAttribute(USEFLASHYELLOW).getBooleanValue());
437            }
438        } catch (IllegalArgumentException e) {
439            log.error("An error occurred in retrieving the watched signal 1 ({})element attribute list for {}", block.getAttributeValue(WATCHEDSIGNAL_1), bb.getDrivenSignal());
440            result = false;
441        }
442        return result;
443    }
444
445    private boolean loadWatchedTurnout(Element block, BlockBossLogic bb) {
446        boolean result = true;
447        try {
448            if (block.getAttribute(WATCHEDTURNOUT) != null) {
449                bb.setTurnout(block.getAttributeValue(WATCHEDTURNOUT));
450            }
451        } catch (IllegalArgumentException e) {
452            log.error("An error occurred in retrieving the watched turnout ({})element attribute list for {}", block.getAttributeValue(WATCHEDTURNOUT), bb.getDrivenSignal());
453            result = false;
454        }
455        return result;
456    }
457
458    private BlockBossLogic getBlockBossLogicFromElement(Element block) {
459        BlockBossLogic blockBossLogic;
460        String signalName = block.getAttributeValue(SIGNAL);
461        if (signalName == null || signalName.isEmpty()) {
462            // this is an error
463            log.error("Ignoring a <signalelement> element with no signal attribute value");
464            return null;
465        }
466
467        if (InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName) == null) {
468            // this is an error
469            log.error("SignalHead {} not defined, <signalelement> element referring to it is ignored", signalName);
470            return null;
471        }
472
473        try {
474            blockBossLogic = BlockBossLogic.getStoppedObject(signalName);
475        } catch (IllegalArgumentException e) {
476            // Potential exception in BLockBossProvider:provide via BlockBossLogic.
477            return null;
478        }
479        return blockBossLogic;
480    }
481
482    private void getSensorAttributesUsingSystemName(BlockBossLogic bb, List<Element> sl) {
483        if (sl.size() >= 1 && sl.get(0) != null) {
484            bb.setSensor1(sl.get(0).getAttributeValue(SYSTEM_NAME));
485        }
486        if (sl.size() >= 2 && sl.get(1) != null) {
487            bb.setSensor2(sl.get(1).getAttributeValue(SYSTEM_NAME));
488        }
489        if (sl.size() >= 3 && sl.get(2) != null) {
490            bb.setSensor3(sl.get(2).getAttributeValue(SYSTEM_NAME));
491        }
492        if (sl.size() >= 4 && sl.get(3) != null) {
493            bb.setSensor4(sl.get(3).getAttributeValue(SYSTEM_NAME));
494        }
495        if (sl.size() >= 5 && sl.get(4) != null) {
496            bb.setSensor5(sl.get(4).getAttributeValue(SYSTEM_NAME));
497        }
498    }
499
500    /**
501     * Update static data from XML file
502     *
503     * @param element Top level Element to unpack.
504     * @param o       ignored
505     */
506    @Override
507    public void load(Element element, Object o) {
508        log.error("load(Element, Object) called unexpectedly");
509    }
510
511    @Override
512    public int loadOrder() {
513        return Manager.BLOCKBOSS;
514    }
515
516    private static final Logger log = LoggerFactory.getLogger(BlockBossLogicProviderXml.class);
517
518}