001package jmri.jmrit.display.layoutEditor.configurexml;
002
003import java.awt.Color;
004import java.util.List;
005import jmri.ConfigureManager;
006import jmri.InstanceManager;
007import jmri.Sensor;
008import jmri.jmrit.display.layoutEditor.LayoutBlock;
009import jmri.jmrit.display.layoutEditor.LayoutBlockManager;
010import jmri.util.ColorUtil;
011import org.jdom2.Attribute;
012import org.jdom2.DataConversionException;
013import org.jdom2.Element;
014
015/**
016 * Provides the functionality for configuring a LayoutBlockManager
017 *
018 * @author Dave Duchamp Copyright (c) 2007
019 * @author George Warner Copyright (c) 2017-2019
020 */
021public class LayoutBlockManagerXml extends jmri.managers.configurexml.AbstractNamedBeanManagerConfigXML {
022
023    public LayoutBlockManagerXml() {
024    }
025
026    /**
027     * Implementation for storing the contents of a LayoutBlockManager
028     *
029     * @param o Object to store, of type LayoutBlockManager
030     * @return Element containing the complete info
031     */
032    @Override
033    public Element store(Object o) {
034        Element layoutblocks = new Element("layoutblocks");
035        setStoreElementClass(layoutblocks);
036        LayoutBlockManager tm = (LayoutBlockManager) o;
037        if (tm.isAdvancedRoutingEnabled()) {
038            layoutblocks.setAttribute("blockrouting", "yes");
039        }
040        jmri.NamedBeanHandle<Sensor> tmStable = tm.getNamedStabilisedSensor();
041        if (tmStable != null) {
042            layoutblocks.setAttribute("routingStablisedSensor", tmStable.getName());
043        }
044
045        // don't return an element if there is nothing to include
046        if (tm.getNamedBeanSet().isEmpty()) {
047            return null;
048        }
049        
050        for (LayoutBlock b : tm.getNamedBeanSet()) {
051
052            // save only those LayoutBlocks that are in use--skip abandoned ones
053            if (b!=null && b.getUseCount() > 0) {
054                Element elem = new Element("layoutblock").setAttribute("systemName", b.getSystemName());
055                elem.addContent(new Element("systemName").addContent(b.getSystemName()));
056                storeCommon(b, elem);
057                if (!b.getOccupancySensorName().isEmpty()) {
058                    elem.setAttribute("occupancysensor", b.getOccupancySensorName());
059                }
060                elem.setAttribute("occupiedsense", "" + b.getOccupiedSense());
061                elem.setAttribute("trackcolor", ColorUtil.colorToColorName(b.getBlockTrackColor()));
062                elem.setAttribute("occupiedcolor", ColorUtil.colorToColorName(b.getBlockOccupiedColor()));
063                elem.setAttribute("extracolor", ColorUtil.colorToColorName(b.getBlockExtraColor()));
064                if (!b.getMemoryName().isEmpty()) {
065                    elem.setAttribute("memory", b.getMemoryName());
066                }
067                if (!b.useDefaultMetric()) {
068                    elem.addContent(new Element("metric").addContent("" + b.getBlockMetric()));
069                }
070                layoutblocks.addContent(elem);
071                
072            }
073        }
074        return (layoutblocks);
075    }
076
077    /**
078     * Subclass provides implementation to create the correct top element,
079     * including the type information. Default implementation is to use the
080     * local class here.
081     *
082     * @param layoutblocks The top-level element being created
083     */
084    public void setStoreElementClass(Element layoutblocks) {
085        layoutblocks.setAttribute("class", getClass().getName());
086    }
087
088    @Override
089    public void load(Element element, Object o) {
090        log.error("Invalid method called");
091    }
092
093    @Override
094    public boolean load(Element shared, Element perNode) {
095        // create the master object
096        replaceLayoutBlockManager();
097        // load individual layoutblocks
098        loadLayoutBlocks(shared);
099        return true;
100    }
101
102    /**
103     * Utility method to load the individual LayoutBlock objects. If there's no
104     * additional info needed for a specific layoutblock type, invoke this with
105     * the parent of the set of layoutblock elements.
106     *
107     * @param layoutblocks Element containing the layoutblock elements to load.
108     */
109    public void loadLayoutBlocks(Element layoutblocks) {
110        LayoutBlockManager tm = InstanceManager.getDefault(LayoutBlockManager.class);
111        try {
112            tm.enableAdvancedRouting(layoutblocks.getAttribute("blockrouting").getBooleanValue());
113        } catch (DataConversionException e1) {
114            log.warn("unable to convert layout block manager blockrouting attribute");
115        } catch (NullPointerException e) {  // considered normal if the attribute is not present
116        }
117        if (layoutblocks.getAttribute("routingStablisedSensor") != null) {
118            try {
119                tm.setStabilisedSensor(layoutblocks.getAttribute("routingStablisedSensor").getValue());
120            } catch (jmri.JmriException e) {
121            }
122        }
123
124        List<Element> layoutblockList = layoutblocks.getChildren("layoutblock");
125        if (log.isDebugEnabled()) {
126            log.debug("Found {} layoutblocks", layoutblockList.size());
127        }
128
129        for (Element e : layoutblockList) {
130            String sysName = getSystemName(e);
131            if (sysName == null) {
132                log.warn("unexpected null in systemName {} {}", e, e.getAttributes());
133                break;
134            }
135
136            String userName = getUserName(e);
137            LayoutBlock b = tm.createNewLayoutBlock(sysName, userName);
138
139            // load common parts
140            loadCommon(b, e);
141
142            if (b != null) {
143                // set attributes
144                Color color;
145                try {
146                    color = ColorUtil.stringToColor(e.getAttribute("trackcolor").getValue());
147                    b.setBlockTrackColor(color);
148                } catch (IllegalArgumentException ex) {
149                    b.setBlockTrackColor(Color.darkGray);
150                    log.error("Invalid trackcolor '{}'; using 'darkGray'", e.getAttribute("trackcolor").getValue());
151                }
152                try {
153                    color = ColorUtil.stringToColor(e.getAttribute("occupiedcolor").getValue());
154                    b.setBlockOccupiedColor(color);
155                } catch (IllegalArgumentException ex) {
156                    b.setBlockOccupiedColor(Color.red);
157                    log.error("Invalid occupiedcolor '{}'; using 'red'", e.getAttribute("occupiedcolor").getValue());
158                }
159                Attribute a = e.getAttribute("extracolor");
160                if (a != null) {
161                    try {
162                        b.setBlockExtraColor(ColorUtil.stringToColor(a.getValue()));
163                    } catch (IllegalArgumentException ex) {
164                        b.setBlockExtraColor(Color.white);
165                        log.error("Invalid extracolor '{}'; using 'white'", e.getAttribute("extracolor").getValue());
166                    }
167                }
168                a = e.getAttribute("occupancysensor");
169                if (a != null) {
170                    b.setOccupancySensorName(a.getValue());
171                }
172                a = e.getAttribute("memory");
173                if (a != null) {
174                    b.setMemoryName(a.getValue());
175                }
176                a = e.getAttribute("occupancysensorsense");
177                int sense = Sensor.ACTIVE;
178                try {
179                    sense = e.getAttribute("occupiedsense").getIntValue();
180                } catch (org.jdom2.DataConversionException ex) {
181                    log.error("failed to convert occupiedsense attribute");
182                }
183                b.setOccupiedSense(sense);
184                if (e.getChild("metric") != null) {
185                    String stMetric = e.getChild("metric").getText();
186                    try {
187                        b.setBlockMetric(Integer.parseInt(stMetric));
188                    } catch (java.lang.NumberFormatException ex) {
189                        log.error("failed to convert metric attribute for block {}", b.getDisplayName());
190                    }
191                }
192            }
193        }
194    }
195
196    /**
197     * Replace the current LayoutBlockManager, if there is one, with one newly
198     * created during a load operation. This is skipped if they are of the same
199     * absolute type.
200     */
201    protected void replaceLayoutBlockManager() {
202        if (InstanceManager.getDefault(LayoutBlockManager.class).getClass().getName()
203                .equals(LayoutBlockManager.class.getName())) {
204            return;
205        }
206        // if old manager exists, remove it from configuration process
207        if (InstanceManager.getNullableDefault(LayoutBlockManager.class) != null) {
208            InstanceManager.getDefault(jmri.ConfigureManager.class).deregister(
209                    InstanceManager.getDefault(LayoutBlockManager.class));
210        }
211
212        // register new one with InstanceManager
213        LayoutBlockManager pManager = InstanceManager.getDefault(LayoutBlockManager.class);
214        // register new one for configuration
215        ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
216        if (cm != null) {
217            cm.registerConfig(pManager, jmri.Manager.LAYOUTBLOCKS);
218        }
219    }
220
221    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutBlockManagerXml.class);
222}