001package jmri.managers.configurexml;
002
003import java.util.List;
004import java.util.SortedSet;
005
006import jmri.Block;
007import jmri.EntryPoint;
008import jmri.InstanceManager;
009import jmri.Section;
010import jmri.SectionManager;
011
012import org.jdom2.DataConversionException;
013import org.jdom2.Element;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * Provides the functionality for persistence of a SectionManager.
019 *
020 * @author Dave Duchamp Copyright (c) 2008
021 */
022public class DefaultSectionManagerXml extends jmri.managers.configurexml.AbstractNamedBeanManagerConfigXML {
023
024    public DefaultSectionManagerXml() {
025    }
026
027    /**
028     * Implementation for storing the contents of a SectionManager.
029     *
030     * @param o Object to store, of type SectionManager
031     * @return Element containing the complete info
032     */
033    @Override
034    public Element store(Object o) {
035        Element sections = new Element("sections");
036        setStoreElementClass(sections);
037        SectionManager sctm = (SectionManager) o;
038        if (sctm != null) {
039            SortedSet<Section> sctList = sctm.getNamedBeanSet();
040            // don't return an element if there are no Sections to include
041            if (sctList.isEmpty()) {
042                return null;
043            }
044
045            // store the Sections
046            for (Section x : sctList) {
047                if (x == null) {
048                    log.error("Memory null during store, skipped");
049                    break;
050                }
051                String sName = x.getSystemName();
052                log.debug("Section system name is {}", sName);
053
054                if (x.getSectionType() != Section.DYNAMICADHOC) {
055                    Element elem = new Element("section");
056                    elem.addContent(new Element("systemName").addContent(sName));
057
058                    // As a work-around for backward compatibility, store systemName and username as attribute.
059                    // TODO Remove this in e.g. JMRI 4.11.1 and then update all the loadref comparison files
060                    elem.setAttribute("systemName", sName);
061                    String uname = x.getUserName();
062                    if (uname != null && !uname.isEmpty()) {
063                        elem.setAttribute("userName", uname);
064                    }
065
066                    // store common part
067                    storeCommon(x, elem);
068
069                    String txt = "userdefined";
070                    if (x.getSectionType() == Section.SIGNALMASTLOGIC) {
071                        txt = "signalmastlogic";
072                    }
073                    elem.setAttribute("creationtype", txt);
074                    txt = x.getForwardStoppingSensorName();
075                    if ((txt != null) && (!txt.isEmpty())) {
076                        elem.setAttribute("fstopsensorname", txt);
077                    }
078                    txt = x.getReverseStoppingSensorName();
079                    if ((txt != null) && (!txt.isEmpty())) {
080                        elem.setAttribute("rstopsensorname", txt);
081                    }
082                    txt = x.getForwardBlockingSensorName();
083                    if ((txt != null) && (!txt.isEmpty())) {
084                        elem.setAttribute("fsensorname", txt);
085                    }
086                    txt = x.getReverseBlockingSensorName();
087                    if ((txt != null) && (!txt.isEmpty())) {
088                        elem.setAttribute("rsensorname", txt);
089                    }
090                    if (x.getSectionType() == Section.USERDEFINED) {
091                        // save child block entries
092                        int index = 0;
093                        Block b = x.getBlockBySequenceNumber(index);
094                        Element bElem;
095                        while (b != null) {
096                            bElem = new Element("blockentry");
097                            bElem.setAttribute("sName", b.getSystemName());
098                            bElem.setAttribute("order", Integer.toString(index));
099                            elem.addContent(bElem);
100                            index++;
101                            b = x.getBlockBySequenceNumber(index);
102                        }
103                        // save child entry points
104                        List<EntryPoint> epList = x.getEntryPointList();
105                        Element epElem;
106                        int i = 0;
107                        for (EntryPoint ep : epList) {
108                            if (ep != null) {
109                                epElem = new Element("entrypoint");
110                                // add some protection against a reading problem
111                                if (ep.getFromBlock() == null) {
112                                    log.error("Unexpected null getFromBlock while storing ep {} in Section {}, skipped", i, sName);
113                                    break;
114                                }
115                                epElem.setAttribute("fromblock", ep.getFromBlock().getSystemName());
116                                if (ep.getBlock() == null) {
117                                    log.error("Unexpected null getBlock while storing ep {} in Section {}, skipped", i, sName);
118                                    break;
119                                }
120                                epElem.setAttribute("toblock", ep.getBlock().getSystemName());
121                                epElem.setAttribute("direction", Integer.toString(ep.getDirection()));
122                                epElem.setAttribute("fixed", "" + (ep.isFixed() ? "yes" : "no"));
123                                epElem.setAttribute("fromblockdirection", "" + ep.getFromBlockDirection());
124                                elem.addContent(epElem);
125                                i++;
126                            }
127                        }
128                    }
129                    sections.addContent(elem);
130                }
131            }
132        }
133        return (sections);
134    }
135
136    /**
137     * Subclass provides implementation to create the correct top element,
138     * including the type information. Default implementation is to use the
139     * local class here.
140     *
141     * @param sections The top-level element being created
142     */
143    public void setStoreElementClass(Element sections) {
144        sections.setAttribute("class", "jmri.configurexml.SectionManagerXml");
145    }
146
147    @Override
148    public void load(Element element, Object o) {
149        log.error("Invalid method called");
150    }
151
152    /**
153     * Create a SectionManager object of the correct class, then register and
154     * fill it.
155     *
156     * @param sharedSections  Top level Element to unpack.
157     * @param perNodeSections Per-node Element to unpack.
158     * @return true if successful
159     */
160    @Override
161    public boolean load(Element sharedSections, Element perNodeSections) {
162        // load individual Sections
163        loadSections(sharedSections, perNodeSections);
164        return true;
165    }
166
167    /**
168     * Utility method to load the individual Section objects. If there's no
169     * additional info needed for a specific Section type, invoke this with the
170     * parent of the set of Section elements.
171     *
172     * @param sharedSections  Element containing the Section elements to load.
173     * @param perNodeSections Per-node Element containing the Section elements
174     *                        to load.
175     */
176    public void loadSections(Element sharedSections, Element perNodeSections) {
177        List<Element> sectionList = sharedSections.getChildren("section");
178        log.debug("Found {} Sections", sectionList.size());
179        SectionManager sctm = InstanceManager.getDefault(jmri.SectionManager.class);
180        sctm.setPropertyChangesSilenced("beans", true);
181
182        for (Element s : sectionList) {
183            String sysName = getSystemName(s);
184            String userName = getUserName(s);
185            Section x;
186            try {
187                x = sctm.createNewSection(sysName, userName);
188            }
189            catch (IllegalArgumentException ex){
190                log.error("Unable to create Section {} {}",sysName,ex.getMessage());
191                continue;
192            }
193            // load common part
194            loadCommon(x, (s));
195
196            if (s.getAttribute("creationtype") != null) {
197                String creationType = s.getAttribute("creationtype").getValue();
198                if (creationType.equals("userdefined")) {
199                    x.setSectionType(Section.USERDEFINED);
200                } else if (creationType.equals("signalmastlogic")) {
201                    x.setSectionType(Section.SIGNALMASTLOGIC);
202                }
203            }
204            if (s.getAttribute("fsensorname") != null) {
205                String forName = s.getAttribute("fsensorname").getValue();
206                x.delayedSetForwardBlockingSensorName(forName);
207            }
208            if (s.getAttribute("rsensorname") != null) {
209                String revName = s.getAttribute("rsensorname").getValue();
210                x.delayedSetReverseBlockingSensorName(revName);
211            }
212            if (s.getAttribute("fstopsensorname") != null) {
213                String forName = s.getAttribute("fstopsensorname").getValue();
214                x.delayedSetForwardStoppingSensorName(forName);
215            }
216            if (s.getAttribute("rstopsensorname") != null) {
217                String revName = s.getAttribute("rstopsensorname").getValue();
218                x.delayedSetReverseStoppingSensorName(revName);
219            }
220
221            // load block entry children
222            List<Element> sectionBlockList = s.getChildren("blockentry");
223            for (Element elem : sectionBlockList) {
224                x.delayedAddBlock(elem.getAttribute("sName").getValue());
225                // insert code here to verify sequence number if needed in the future
226            }
227
228            // load entry point children
229            List<Element> sectionEntryPointList = s.getChildren("entrypoint");
230            for (Element elem : sectionEntryPointList) {
231                String blockName = elem.getAttribute("toblock").getValue();
232                String fromBlockName = elem.getAttribute("fromblock").getValue();
233                String fromBlockDirection = "";
234                if (elem.getAttribute("fromblockdirection") != null) {
235                    fromBlockDirection = elem.getAttribute("fromblockdirection").getValue();
236                }
237                EntryPoint ep = new EntryPoint(blockName, fromBlockName, fromBlockDirection);
238                try {
239                    ep.setDirection(elem.getAttribute("direction").getIntValue());
240                } catch (DataConversionException e) {
241                    log.error("Data Conversion Exception when loading direction of entry point - ", e);
242                }
243                boolean fixed = true;
244                if (elem.getAttribute("fixed").getValue().equals("no")) {
245                    fixed = false;
246                }
247                ep.setFixed(fixed);
248                if (ep.isForwardType()) {
249                    x.addToForwardList(ep);
250                } else if (ep.isReverseType()) {
251                    x.addToReverseList(ep);
252                }
253            }
254        }
255        sctm.setPropertyChangesSilenced("beans", false);
256    }
257
258    @Override
259    public int loadOrder() {
260        return InstanceManager.getDefault(jmri.SectionManager.class).getXMLOrder();
261    }
262
263    private final static Logger log = LoggerFactory.getLogger(DefaultSectionManagerXml.class);
264
265}