001package jmri.managers.configurexml;
002
003import java.util.List;
004import java.util.SortedSet;
005
006import jmri.InstanceManager;
007import jmri.Memory;
008import jmri.MemoryManager;
009import jmri.configurexml.JmriConfigureXmlException;
010import jmri.configurexml.LoadAndStorePreferences;
011import jmri.jmrit.roster.RosterEntry;
012
013import org.jdom2.Element;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * Provides the abstract base and store functionality for configuring
019 * MemoryManagers, working with AbstractMemoryManagers.
020 * <p>
021 * Also serves as base class for {@link jmri.configurexml.BlockManagerXml} persistence.
022 * <p>
023 * Typically, a subclass will just implement the load(Element memories) class,
024 * relying on implementation here to load the individual Memory objects. Note
025 * that these are stored explicitly, so the resolution mechanism doesn't need to
026 * see *Xml classes for each specific Memory or AbstractMemory subclass at store
027 * time.
028 *
029 * @author Bob Jacobsen Copyright: Copyright (c) 2002, 2008
030 */
031public abstract class AbstractMemoryManagerConfigXML extends AbstractNamedBeanManagerConfigXML {
032
033    public AbstractMemoryManagerConfigXML() {
034    }
035
036    /**
037     * Default implementation for storing the contents of a MemoryManager.
038     *
039     * @param o Object to store, of type MemoryManager
040     * @return Element containing the complete info
041     */
042    @Override
043    public Element store(Object o) {
044        Element memories = new Element("memories");
045        setStoreElementClass(memories);
046        MemoryManager mm = (MemoryManager) o;
047        if (mm != null) {
048            SortedSet<Memory> memList = mm.getNamedBeanSet();
049            // don't return an element if there are no memories to include
050            if (memList.isEmpty()) {
051                return null;
052            }
053            // store the memories
054            for (Memory m : memList) {
055                String mName = m.getSystemName();
056                log.debug("system name is {}", mName);
057
058                Element elem = new Element("memory");
059                elem.addContent(new Element("systemName").addContent(mName));
060
061                // store common part
062                storeCommon(m, elem);
063
064                var loadAndStorePreferences = InstanceManager.getDefault(LoadAndStorePreferences.class);
065                // store value if non-null; null values omitted
066                if (! (loadAndStorePreferences.isExcludeMemoryIMCURRENTTIME()
067                        && mName.equals("IMCURRENTTIME")) ) {
068                    Object obj = m.getValue();
069                    if (obj != null) {
070                        if (obj instanceof RosterEntry) {
071                            String valueClass = obj.getClass().getName();
072                            String value = ((RosterEntry) obj).getId();
073                            elem.setAttribute("value", value);
074                            elem.setAttribute("valueClass", valueClass);
075                        } else {
076                            String value = obj.toString();
077                            elem.setAttribute("value", value);
078                        }
079                    }
080                }
081
082                log.debug("store Memory {}", mName);
083                memories.addContent(elem);
084            }
085        }
086        return memories;
087    }
088
089    /**
090     * Subclass provides implementation to create the correct top element,
091     * including the type information. Default implementation is to use the
092     * local class here.
093     *
094     * @param memories The top-level element being created
095     */
096    abstract public void setStoreElementClass(Element memories);
097
098    /**
099     * Create a MemoryManager object of the correct class, then register and
100     * fill it.
101     *
102     * @param sharedMemories  Shared top level Element to unpack.
103     * @param perNodeMemories Per-node top level Element to unpack.
104     * @return true if successful
105     * @throws jmri.configurexml.JmriConfigureXmlException if error during load.
106     */
107    @Override
108    abstract public boolean load(Element sharedMemories, Element perNodeMemories) throws JmriConfigureXmlException;
109
110    /**
111     * Utility method to load the individual Memory objects. If there's no
112     * additional info needed for a specific Memory type, invoke this with the
113     * parent of the set of Memory elements.
114     *
115     * @param memories Element containing the Memory elements to load.
116     */
117    public void loadMemories(Element memories) {
118        List<Element> memoryList = memories.getChildren("memory");
119        log.debug("Found {} Memory objects", memoryList.size());
120        MemoryManager mm = InstanceManager.memoryManagerInstance();
121
122        for (Element el : memoryList) {
123            String sysName = getSystemName(el);
124            if (sysName == null) {
125                log.warn("unexpected null in systemName {}", (el));
126                break;
127            }
128
129            String userName = getUserName(el);
130
131            checkNameNormalization(sysName, userName, mm);
132
133            log.debug("create Memory: ({})({})", sysName, (userName == null ? "<null>" : userName));
134            Memory m = mm.newMemory(sysName, userName);
135            if (el.getAttribute("value") != null) {
136                loadValue(el, m);
137            }
138            // load common parts
139            loadCommon(m, el);
140        }
141    }
142
143    @Override
144    public int loadOrder() {
145        return InstanceManager.memoryManagerInstance().getXMLOrder();
146    }
147
148    private void loadValue(Element memory, Memory m) {
149        String value = memory.getAttribute("value").getValue();
150        if (memory.getAttribute("valueClass") != null) {
151            String adapter = memory.getAttribute("valueClass").getValue();
152            if (adapter.equals("jmri.jmrit.roster.RosterEntry")) {
153                RosterEntry re = jmri.jmrit.roster.Roster.getDefault().getEntryForId(value);
154                m.setValue(re);
155                return;
156            }
157        }
158        m.setValue(value);
159    }
160
161    private final static Logger log = LoggerFactory.getLogger(AbstractMemoryManagerConfigXML.class);
162
163}