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.isExcludeMemoryContents() )) {
067                    if (! (loadAndStorePreferences.isExcludeMemoryIMCURRENTTIME()
068                            && mName.equals("IMCURRENTTIME")) ) {
069                        Object obj = m.getValue();
070                        if (obj != null) {
071                            if (obj instanceof RosterEntry) {
072                                String valueClass = obj.getClass().getName();
073                                String value = ((RosterEntry) obj).getId();
074                                elem.setAttribute("value", value);
075                                elem.setAttribute("valueClass", valueClass);
076                            } else {
077                                String value = obj.toString();
078                                elem.setAttribute("value", value);
079                            }
080                        }
081                    }
082                }
083
084                log.debug("store Memory {}", mName);
085                memories.addContent(elem);
086            }
087        }
088        return memories;
089    }
090
091    /**
092     * Subclass provides implementation to create the correct top element,
093     * including the type information. Default implementation is to use the
094     * local class here.
095     *
096     * @param memories The top-level element being created
097     */
098    abstract public void setStoreElementClass(Element memories);
099
100    /**
101     * Create a MemoryManager object of the correct class, then register and
102     * fill it.
103     *
104     * @param sharedMemories  Shared top level Element to unpack.
105     * @param perNodeMemories Per-node top level Element to unpack.
106     * @return true if successful
107     * @throws jmri.configurexml.JmriConfigureXmlException if error during load.
108     */
109    @Override
110    abstract public boolean load(Element sharedMemories, Element perNodeMemories) throws JmriConfigureXmlException;
111
112    /**
113     * Utility method to load the individual Memory objects. If there's no
114     * additional info needed for a specific Memory type, invoke this with the
115     * parent of the set of Memory elements.
116     *
117     * @param memories Element containing the Memory elements to load.
118     */
119    public void loadMemories(Element memories) {
120        List<Element> memoryList = memories.getChildren("memory");
121        log.debug("Found {} Memory objects", memoryList.size());
122        MemoryManager mm = InstanceManager.memoryManagerInstance();
123
124        for (Element el : memoryList) {
125            String sysName = getSystemName(el);
126            if (sysName == null) {
127                log.warn("unexpected null in systemName {}", (el));
128                break;
129            }
130
131            String userName = getUserName(el);
132
133            checkNameNormalization(sysName, userName, mm);
134
135            log.debug("create Memory: ({})({})", sysName, (userName == null ? "<null>" : userName));
136            Memory m = mm.newMemory(sysName, userName);
137            if (el.getAttribute("value") != null) {
138                loadValue(el, m);
139            }
140            // load common parts
141            loadCommon(m, el);
142        }
143    }
144
145    @Override
146    public int loadOrder() {
147        return InstanceManager.memoryManagerInstance().getXMLOrder();
148    }
149
150    private void loadValue(Element memory, Memory m) {
151        String value = memory.getAttribute("value").getValue();
152        if (memory.getAttribute("valueClass") != null) {
153            String adapter = memory.getAttribute("valueClass").getValue();
154            if (adapter.equals("jmri.jmrit.roster.RosterEntry")) {
155                RosterEntry re = jmri.jmrit.roster.Roster.getDefault().getEntryForId(value);
156                m.setValue(re);
157                return;
158            }
159        }
160        m.setValue(value);
161    }
162
163    private final static Logger log = LoggerFactory.getLogger(AbstractMemoryManagerConfigXML.class);
164
165}