001package jmri.managers.configurexml;
002
003import java.util.List;
004import java.util.SortedSet;
005
006import jmri.ConfigureManager;
007import jmri.InstanceManager;
008import jmri.Manager;
009import jmri.SignalHead;
010import jmri.SignalHeadManager;
011import jmri.configurexml.ConfigXmlManager;
012import jmri.jmrix.internal.InternalSystemConnectionMemo;
013import jmri.managers.AbstractSignalHeadManager;
014import org.jdom2.Element;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018/**
019 * Provides the abstract base and store functionality for configuring
020 * SignalHeadManagers, working with AbstractSignalHeadManagers.
021 * <p>
022 * Typically, a subclass will just implement the load(Element turnouts) class,
023 * relying on implementation here to load the individual turnouts. Note that
024 * these are stored explicitly, so the resolution mechanism doesn't need to see
025 * *Xml classes for each specific SignalHead or AbstractSignalHead subclass at
026 * store time.
027 * <p>
028 * Based on {@link AbstractTurnoutManagerConfigXML}
029 *
030 * @author Bob Jacobsen Copyright: Copyright (c) 2003, 2008
031 */
032public class AbstractSignalHeadManagerXml extends AbstractNamedBeanManagerConfigXML {
033
034    public AbstractSignalHeadManagerXml() {
035    }
036
037    /**
038     * Default implementation for storing the contents of a SignalHeadManager.
039     * <p>
040     * Unlike most other managers, the individual SignalHead objects are stored
041     * separately via the configuration system so they can have separate type
042     * information.
043     *
044     * @param o Object to store, of type SignalHeadManager
045     * @return Element containing the complete info
046     */
047    @Override
048    public Element store(Object o) {
049        Element signalheads = new Element("signalheads");
050        setStoreElementClass(signalheads);
051        SignalHeadManager shm = (SignalHeadManager) o;
052        if (shm != null) {
053            SortedSet<SignalHead> shList = shm.getNamedBeanSet();
054            // don't return an element if there are no signalheads to include
055            if (shList.isEmpty()) {
056                return null;
057            }
058            for (SignalHead sh : shList) {
059                // store the signalheads
060                String shName = sh.getSystemName();
061                log.debug("system name is {}", shName);
062                Element e = ConfigXmlManager.elementFromObject(sh);
063                if (e != null) {
064                    signalheads.addContent(e);
065                }
066            }
067        }
068        return signalheads;
069    }
070
071    /**
072     * Subclass provides implementation to create the correct top element,
073     * including the type information. Default implementation is to use the
074     * local class here.
075     *
076     * @param turnouts The top-level element being created
077     */
078    public void setStoreElementClass(Element turnouts) {
079        turnouts.setAttribute("class", this.getClass().getName());
080    }
081
082    /**
083     * Create a SignalHeadManager object of the correct class, then register and
084     * fill it.
085     *
086     * @param shared  Shared top level Element to unpack.
087     * @param perNode Per-node top level Element to unpack.
088     * @return true if successful
089     */
090    @Override
091    public boolean load(Element shared, Element perNode) {
092        // create the master object
093        replaceSignalHeadManager();
094
095        // load individual turnouts
096        loadSignalHeads(shared, perNode);
097        return true;
098    }
099
100    /**
101     * Utility method to load the individual SignalHead objects. If there's no
102     * additional info needed for a specific signal head type, invoke this with
103     * the parent of the set of SignalHead elements.
104     *
105     * @param shared  Element containing the SignalHead elements to load.
106     * @param perNode Element containing any per-node information associated
107     *                with the shared Element.
108     */
109    public void loadSignalHeads(Element shared, Element perNode) {
110        InstanceManager.getDefault(SignalHeadManager.class);
111        List<Element> headClassList = shared.getChildren();
112        log.debug("Found {} signal heads", headClassList.size());
113        // load the contents
114        boolean result = loadInAdapter(headClassList, null);
115        if (!result) {
116            log.warn("error loading signalheads");
117        }
118    }
119
120    /**
121     * Replace the current signal head manager, if there is one, with one newly
122     * created during a load operation. This is skipped if they are of the same
123     * absolute type.
124     */
125    protected void replaceSignalHeadManager() {
126        if (InstanceManager.getDefault(SignalHeadManager.class).getClass().getName()
127                .equals(AbstractSignalHeadManager.class.getName())) {
128            return;
129        }
130        // if old manager exists, remove it from configuration process
131        InstanceManager.getOptionalDefault(SignalHeadManager.class).ifPresent((shm) -> {
132            InstanceManager.getDefault(ConfigureManager.class).deregister(shm);
133        });
134
135        // register new one with InstanceManager
136        AbstractSignalHeadManager pManager = new AbstractSignalHeadManager(InstanceManager.getDefault(InternalSystemConnectionMemo.class));
137        InstanceManager.setDefault(SignalHeadManager.class, pManager);
138        // register new one for configuration
139        InstanceManager.getOptionalDefault(ConfigureManager.class).ifPresent((cm) -> {
140            cm.registerConfig(pManager, Manager.SIGNALHEADS);
141        });
142    }
143
144    @Override
145    public int loadOrder() {
146        return InstanceManager.getDefault(SignalHeadManager.class).getXMLOrder();
147    }
148
149    private final static Logger log = LoggerFactory.getLogger(AbstractSignalHeadManagerXml.class);
150
151}