001package jmri.jmrit.display.layoutEditor.configurexml;
002
003import java.awt.geom.Point2D;
004
005import jmri.Turnout;
006import jmri.configurexml.AbstractXmlAdapter;
007import jmri.jmrit.display.layoutEditor.*;
008import org.jdom2.Attribute;
009import org.jdom2.DataConversionException;
010import org.jdom2.Element;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * This module handles configuration for display.LayoutTurnout objects for a
016 * LayoutEditor.
017 *
018 * @author David Duchamp Copyright (c) 2007
019 * @author George Warner Copyright (c) 2017-2019
020 * @author Bob Jacobsen Copyright (c) 2020
021 */
022public class LayoutTurnoutViewXml extends AbstractXmlAdapter {
023
024    static final EnumIO<LayoutTurnout.LinkType> linkEnumMap = new EnumIoNamesNumbers<>(LayoutTurnout.LinkType.class);
025    static final EnumIO<LayoutTurnout.TurnoutType> tTypeEnumMap = new EnumIoNamesNumbers<>(LayoutTurnout.TurnoutType.class);
026    
027    public LayoutTurnoutViewXml() {
028    }
029
030    protected void addClass(Element element) {
031        element.setAttribute("class", "jmri.jmrit.display.layoutEditor.configurexml.LayoutTurnoutXml");
032    }
033
034    /**
035     * Default implementation for storing the contents of a LayoutTurnout
036     *
037     * @param o Object to store, of type LayoutTurnout
038     * @return Element containing the complete info
039     */
040    @Override
041    public Element store(Object o) {
042
043        LayoutTurnoutView pv = (LayoutTurnoutView) o;
044        LayoutTurnout p = pv.getLayoutTurnout();
045
046        Element element = new Element("layoutturnout");
047
048        // include attributes
049        element.setAttribute("ident", p.getName());
050        element.setAttribute("type", tTypeEnumMap.outputFromEnum(p.getTurnoutType()));
051
052        element.setAttribute("hidden", "" + (pv.isHidden() ? "yes" : "no"));
053        element.setAttribute("disabled", "" + (p.isDisabled() ? "yes" : "no"));
054        element.setAttribute("disableWhenOccupied", "" + (p.isDisabledWhenOccupied() ? "yes" : "no"));
055
056        element.setAttribute("continuing", "" + p.getContinuingSense());
057
058        Point2D coords = pv.getCoordsCenter();
059        element.setAttribute("xcen", "" + coords.getX());
060        element.setAttribute("ycen", "" + coords.getY());
061        
062        coords = pv.getCoordsA();
063        element.setAttribute("xa", "" + coords.getX());
064        element.setAttribute("ya", "" + coords.getY());
065        
066        coords = pv.getCoordsB();
067        element.setAttribute("xb", "" + coords.getX());
068        element.setAttribute("yb", "" + coords.getY());
069        
070        coords = pv.getCoordsC();
071        element.setAttribute("xc", "" + coords.getX());
072        element.setAttribute("yc", "" + coords.getY());
073        
074        coords = pv.getCoordsD();
075        element.setAttribute("xd", "" + coords.getX());
076        element.setAttribute("yd", "" + coords.getY());
077        
078        element.setAttribute("ver", "" + p.getVersion());
079        
080        addClass(element);
081
082        if (!p.getTurnoutName().isEmpty()) {
083            element.setAttribute("turnoutname", p.getTurnoutName());
084        }
085        if (!p.getSecondTurnoutName().isEmpty()) {
086            element.setAttribute("secondturnoutname", p.getSecondTurnoutName());
087            if (p.isSecondTurnoutInverted()) {
088                element.setAttribute("secondturnoutinverted", "true");
089            }
090        }
091
092        if (!p.getLinkedTurnoutName().isEmpty()) {
093            element.setAttribute("linkedturnoutname", p.getLinkedTurnoutName());
094            element.setAttribute("linktype", "" + linkEnumMap.outputFromEnum(p.getLinkType()));
095        }
096
097        if (!p.getBlockName().isEmpty()) {
098            element.setAttribute("blockname", p.getBlockName());
099        }
100        // Only save these if they're different from block A
101        if (!p.getBlockBName().isEmpty() && (!p.getBlockBName().equals(p.getBlockName()))) {
102            element.setAttribute("blockbname", p.getBlockBName());
103        }
104        if (!p.getBlockCName().isEmpty() && (!p.getBlockCName().equals(p.getBlockName()))) {
105            element.setAttribute("blockcname", p.getBlockCName());
106        }
107        if (!p.getBlockDName().isEmpty() && (!p.getBlockDName().equals(p.getBlockName()))) {
108            element.setAttribute("blockdname", p.getBlockDName());
109        }
110
111        if (p.getConnectA() != null) {
112            element.setAttribute("connectaname", ((TrackSegment) p.getConnectA()).getId());
113        }
114        if (p.getConnectB() != null) {
115            element.setAttribute("connectbname", ((TrackSegment) p.getConnectB()).getId());
116        }
117        if (p.getConnectC() != null) {
118            element.setAttribute("connectcname", ((TrackSegment) p.getConnectC()).getId());
119        }
120        if (p.getConnectD() != null) {
121            element.setAttribute("connectdname", ((TrackSegment) p.getConnectD()).getId());
122        }
123
124        if (!p.getSignalA1Name().isEmpty()) {
125            element.setAttribute("signala1name", p.getSignalA1Name());
126        }
127        if (!p.getSignalA2Name().isEmpty()) {
128            element.setAttribute("signala2name", p.getSignalA2Name());
129        }
130        if (!p.getSignalA3Name().isEmpty()) {
131            element.setAttribute("signala3name", p.getSignalA3Name());
132        }
133        if (!p.getSignalB1Name().isEmpty()) {
134            element.setAttribute("signalb1name", p.getSignalB1Name());
135        }
136        if (!p.getSignalB2Name().isEmpty()) {
137            element.setAttribute("signalb2name", p.getSignalB2Name());
138        }
139        if (!p.getSignalC1Name().isEmpty()) {
140            element.setAttribute("signalc1name", p.getSignalC1Name());
141        }
142        if (!p.getSignalC2Name().isEmpty()) {
143            element.setAttribute("signalc2name", p.getSignalC2Name());
144        }
145        if (!p.getSignalD1Name().isEmpty()) {
146            element.setAttribute("signald1name", p.getSignalD1Name());
147        }
148        if (!p.getSignalD2Name().isEmpty()) {
149            element.setAttribute("signald2name", p.getSignalD2Name());
150        }
151
152        if (!p.getSignalAMastName().isEmpty()) {
153            element.addContent(new Element("signalAMast").addContent(p.getSignalAMastName()));
154        }
155
156        if (!p.getSignalBMastName().isEmpty()) {
157            element.addContent(new Element("signalBMast").addContent(p.getSignalBMastName()));
158        }
159        if (!p.getSignalCMastName().isEmpty()) {
160            element.addContent(new Element("signalCMast").addContent(p.getSignalCMastName()));
161        }
162        if (!p.getSignalDMastName().isEmpty()) {
163            element.addContent(new Element("signalDMast").addContent(p.getSignalDMastName()));
164        }
165
166        if (!p.getSensorAName().isEmpty()) {
167            element.addContent(new Element("sensorA").addContent(p.getSensorAName()));
168        }
169
170        if (!p.getSensorBName().isEmpty()) {
171            element.addContent(new Element("sensorB").addContent(p.getSensorBName()));
172        }
173        if (!p.getSensorCName().isEmpty()) {
174            element.addContent(new Element("sensorC").addContent(p.getSensorCName()));
175        }
176        if (!p.getSensorDName().isEmpty()) {
177            element.addContent(new Element("sensorD").addContent(p.getSensorDName()));
178        }
179        return element;
180    }
181
182    @Override
183    public boolean load(Element shared, Element perNode) {
184        log.error("Invalid method called");
185        return false;
186    }
187
188    /**
189     * Load, starting with the levelxing element, then all the other data
190     *
191     * @param element Top level Element to unpack.
192     * @param o       LayoutEditor as an Object
193     */
194    @Override
195    public void load(Element element, Object o) {
196        // create the objects
197        LayoutEditor p = (LayoutEditor) o;
198
199        // get center point
200        String name = element.getAttribute("ident").getValue();
201        double x = 0.0;
202        double y = 0.0;
203        LayoutTurnout.TurnoutType type = LayoutTurnout.TurnoutType.NONE;
204        try {
205            x = element.getAttribute("xcen").getFloatValue();
206            y = element.getAttribute("ycen").getFloatValue();
207            type = tTypeEnumMap.inputFromAttribute(element.getAttribute("type"));
208        } catch (org.jdom2.DataConversionException e) {
209            log.error("failed to convert layoutturnout attribute");
210        }
211
212        int version = 1;
213        try {
214            if (element.getAttribute("ver") != null) {
215                version = element.getAttribute("ver").getIntValue();
216            }
217        } catch (org.jdom2.DataConversionException e) {
218            log.error("failed to convert layoutturnout version attribute");
219        }
220
221        // create the new LayoutTurnout of the correct type
222        LayoutTurnoutView lv; 
223        LayoutTurnout l;
224        
225        switch(type) {
226
227            case RH_TURNOUT :
228                LayoutRHTurnout lrht = new LayoutRHTurnout(name, p, version); 
229                l = lrht;
230                lv = new LayoutRHTurnoutView(lrht, new Point2D.Double(x, y), 0.0, 1.0, 1.0, p);
231                break;
232            case LH_TURNOUT :
233                LayoutLHTurnout llht = new LayoutLHTurnout(name, p, version);
234                l = llht;
235                lv = new LayoutLHTurnoutView(llht, new Point2D.Double(x, y), 0.0, 1.0, 1.0, p);
236                break;
237            case WYE_TURNOUT :
238                LayoutWye lwt = new LayoutWye(name, p, version);
239                l = lwt;
240                lv = new LayoutWyeView(lwt, new Point2D.Double(x, y), 0.0, 1.0, 1.0, p);
241                break;
242            case DOUBLE_XOVER :
243                LayoutDoubleXOver ldx = new LayoutDoubleXOver(name, p, version);
244                l = ldx;
245                lv = new LayoutDoubleXOverView(ldx, new Point2D.Double(x, y), 0.0, 1.0, 1.0, p);
246                break;
247            case RH_XOVER :
248                LayoutRHXOver lrx = new LayoutRHXOver(name, p, version);
249                l = lrx;
250                lv = new LayoutRHXOverView(lrx, new Point2D.Double(x, y), 0.0, 1.0, 1.0, p);
251                break;
252            case LH_XOVER :
253                LayoutLHXOver llx = new LayoutLHXOver(name, p, version);
254                l = llx;
255                lv = new LayoutLHXOverView(llx, new Point2D.Double(x, y), 0.0, 1.0, 1.0, p);
256                break;
257
258            case DOUBLE_SLIP :
259                LayoutDoubleSlip lds = new LayoutDoubleSlip(name, p);
260                l = lds;
261                lv = new LayoutDoubleSlipView(lds, new Point2D.Double(x, y), 0.0, p);
262                log.error("Found DOUBLE_SLIP in LayoutTrack ctor for element {}", name);
263                break;
264            case SINGLE_SLIP :
265                LayoutSingleSlip lss = new LayoutSingleSlip(name, p);
266                l = lss;
267                lv = new LayoutSingleSlipView(lss, new Point2D.Double(x, y), 0.0, p);
268                log.error("Found SINGLE_SLIP in LayoutTrack ctor for element {}", name);
269                break;
270
271            default:
272                log.error("can't create LayoutTrack {} with type {}", name, type);
273                return; // without creating
274        }
275
276        p.addLayoutTrack(l, lv);
277
278        // get remaining attributes
279        Attribute a = element.getAttribute("turnoutname");
280        if (a != null) {
281            l.setTurnout(a.getValue());
282        }
283        a = element.getAttribute("secondturnoutname");
284        if (a != null) {
285            l.setSecondTurnout(a.getValue());
286            try {
287                l.setSecondTurnoutInverted(element.getAttribute("secondturnoutinverted").getBooleanValue());
288            } catch (DataConversionException e1) {
289                log.warn("unable to convert layout turnout secondturnoutinverted attribute");
290            } catch (NullPointerException e) {  // considered normal if the attribute is not present
291            }
292        }
293
294        a = element.getAttribute("blockname");
295        if (a != null) {
296            l.tBlockAName = a.getValue();
297        }
298        a = element.getAttribute("blockbname");
299        if (a != null) {
300            l.tBlockBName = a.getValue();
301        }
302        a = element.getAttribute("blockcname");
303        if (a != null) {
304            l.tBlockCName = a.getValue();
305        }
306        a = element.getAttribute("blockdname");
307        if (a != null) {
308            l.tBlockDName = a.getValue();
309        }
310
311        a = element.getAttribute("connectaname");
312        if (a != null) {
313            l.connectAName = a.getValue();
314        }
315        a = element.getAttribute("connectbname");
316        if (a != null) {
317            l.connectBName = a.getValue();
318        }
319        a = element.getAttribute("connectcname");
320        if (a != null) {
321            l.connectCName = a.getValue();
322        }
323        a = element.getAttribute("connectdname");
324        if (a != null) {
325            l.connectDName = a.getValue();
326        }
327
328        a = element.getAttribute("signala1name");
329        if (a != null) {
330            l.setSignalA1Name(a.getValue());
331        }
332        a = element.getAttribute("signala2name");
333        if (a != null) {
334            l.setSignalA2Name(a.getValue());
335        }
336        a = element.getAttribute("signala3name");
337        if (a != null) {
338            l.setSignalA3Name(a.getValue());
339        }
340        a = element.getAttribute("signalb1name");
341        if (a != null) {
342            l.setSignalB1Name(a.getValue());
343        }
344        a = element.getAttribute("signalb2name");
345        if (a != null) {
346            l.setSignalB2Name(a.getValue());
347        }
348        a = element.getAttribute("signalc1name");
349        if (a != null) {
350            l.setSignalC1Name(a.getValue());
351        }
352        a = element.getAttribute("signalc2name");
353        if (a != null) {
354            l.setSignalC2Name(a.getValue());
355        }
356        a = element.getAttribute("signald1name");
357        if (a != null) {
358            l.setSignalD1Name(a.getValue());
359        }
360        a = element.getAttribute("signald2name");
361        if (a != null) {
362            l.setSignalD2Name(a.getValue());
363        }
364        a = element.getAttribute("linkedturnoutname");
365        if (a != null) {
366            l.linkedTurnoutName = a.getValue();
367            l.linkType = linkEnumMap.inputFromAttribute(element.getAttribute("linktype"));
368        }
369        a = element.getAttribute("continuing");
370        if (a != null) {
371            int continuing = Turnout.CLOSED;
372            try {
373                continuing = element.getAttribute("continuing").getIntValue();
374            } catch (org.jdom2.DataConversionException e) {
375                log.error("failed to convert continuingsense attribute");
376            }
377            l.setContinuingSense(continuing);
378        }
379        try {
380            l.setDisabled(element.getAttribute("disabled").getBooleanValue());
381        } catch (DataConversionException e1) {
382            log.warn("unable to convert layout turnout disabled attribute");
383        } catch (NullPointerException e) {  // considered normal if the attribute is not present
384        }
385        try {
386            l.setDisableWhenOccupied(element.getAttribute("disableWhenOccupied").getBooleanValue());
387        } catch (DataConversionException e1) {
388            log.warn("unable to convert layout turnout disableWhenOccupied attribute");
389        } catch (NullPointerException e) {  // considered normal if the attribute is not present
390        }
391        try {
392            lv.setHidden(element.getAttribute("hidden").getBooleanValue());
393        } catch (DataConversionException e1) {
394            log.warn("unable to convert layout turnout hidden attribute");
395        } catch (NullPointerException e) {  // considered normal if the attribute is not present
396        }
397
398        if (version == 2) {
399            try {
400                x = element.getAttribute("xa").getFloatValue();
401                y = element.getAttribute("ya").getFloatValue();
402                lv.setCoordsA(new Point2D.Double(x, y));
403            } catch (org.jdom2.DataConversionException e) {
404                log.error("failed to convert layoutturnout b coords attribute");
405            } catch (java.lang.NullPointerException e) {
406                //can be ignored as panel file may not support method
407            }
408        }
409        try {
410            x = element.getAttribute("xb").getFloatValue();
411            y = element.getAttribute("yb").getFloatValue();
412            lv.setCoordsB(new Point2D.Double(x, y));
413        } catch (org.jdom2.DataConversionException e) {
414            log.error("failed to convert layoutturnout b coords attribute");
415        }
416        try {
417            x = element.getAttribute("xc").getFloatValue();
418            y = element.getAttribute("yc").getFloatValue();
419            lv.setCoordsC(new Point2D.Double(x, y));
420        } catch (org.jdom2.DataConversionException e) {
421            log.error("failed to convert layoutturnout c coords attribute");
422        }
423        if (version == 2) {
424            try {
425                x = element.getAttribute("xd").getFloatValue();
426                y = element.getAttribute("yd").getFloatValue();
427                lv.setCoordsD(new Point2D.Double(x, y));
428            } catch (org.jdom2.DataConversionException e) {
429                log.error("failed to convert layoutturnout c coords attribute");
430            } catch (java.lang.NullPointerException e) {
431                //can be ignored as panel file may not support method
432            }
433        }
434
435        l.setSignalAMast(getElement(element, "signalAMast"));
436        l.setSignalBMast(getElement(element, "signalBMast"));
437        l.setSignalCMast(getElement(element, "signalCMast"));
438        l.setSignalDMast(getElement(element, "signalDMast"));
439
440        l.setSensorA(getElement(element, "sensorA"));
441        l.setSensorB(getElement(element, "sensorB"));
442        l.setSensorC(getElement(element, "sensorC"));
443        l.setSensorD(getElement(element, "sensorD"));
444    }
445
446    String getElement(Element el, String child) {
447        if (el.getChild(child) != null) {
448            return el.getChild(child).getText();
449        }
450        return "";
451    }
452
453    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTurnoutViewXml.class);
454}