001package jmri.jmrit.logixng.implementation.configurexml;
002
003import java.lang.reflect.Constructor;
004import java.lang.reflect.InvocationTargetException;
005import java.util.ArrayList;
006import java.util.List;
007
008import jmri.ConfigureManager;
009import jmri.InstanceManager;
010import jmri.jmrit.logixng.*;
011import jmri.jmrit.logixng.implementation.DefaultLogixNGManager;
012
013import org.jdom2.Element;
014
015import jmri.jmrit.logixng.implementation.ClipboardMany;
016import jmri.jmrit.logixng.implementation.DefaultClipboard;
017import jmri.jmrit.logixng.implementation.DefaultLogixNG;
018import jmri.jmrit.logixng.util.LogixNG_Thread;
019import jmri.util.ThreadingUtil;
020
021/**
022 * Provides the functionality for configuring LogixNGManagers
023 *
024 * @author Dave Duchamp Copyright (c) 2007
025 * @author Daniel Bergqvist Copyright (c) 2018
026 */
027public class DefaultLogixNGManagerXml extends jmri.managers.configurexml.AbstractNamedBeanManagerConfigXML {
028
029    public DefaultLogixNGManagerXml() {
030    }
031
032    /**
033     * Default implementation for storing the contents of a LogixNG_Manager
034     *
035     * @param o Object to store, of type LogixNG_Manager
036     * @return Element containing the complete info
037     */
038    @Override
039    public Element store(Object o) {
040        boolean hasData = false;
041        
042        Element logixNGs = new Element("LogixNGs");
043        setStoreElementClass(logixNGs);
044        LogixNG_Manager tm = (LogixNG_Manager) o;
045        if (tm != null) {
046            for (LogixNG_Thread thread : LogixNG_Thread.getThreads()) {
047                Element e = new Element("Thread");  // NOI18N
048                e.addContent(new Element("id").addContent(Integer.toString(thread.getThreadId())));
049                e.addContent(new Element("name").addContent(thread.getThreadName()));
050                logixNGs.addContent(e);
051            }
052
053            for (LogixNG logixNG : tm.getNamedBeanSet()) {
054                log.debug("logixng system name is {}", logixNG.getSystemName() );  // NOI18N
055                boolean enabled = logixNG.isEnabled();
056                boolean inline = logixNG.isInline();
057                Element elem = new Element("LogixNG");  // NOI18N
058                elem.addContent(new Element("systemName").addContent(logixNG.getSystemName()));  // NOI18N
059
060                // store common part
061                storeCommon(logixNG, elem);
062
063                Element e = new Element("ConditionalNGs");
064                for (int i=0; i < logixNG.getNumConditionalNGs(); i++) {
065                    e.addContent(new Element("systemName").addContent(logixNG.getConditionalNG(i).getSystemName()));
066                }
067                elem.addContent(e);
068
069                elem.setAttribute("enabled", enabled ? "yes" : "no");  // NOI18N
070                elem.setAttribute("inline", inline ? "yes" : "no");  // NOI18N
071
072                logixNGs.addContent(elem);
073                hasData = true;
074            }
075
076            Element elemInitializationTable = new Element("InitializationTable");  // NOI18N
077            for (LogixNG logixNG : InstanceManager.getDefault(LogixNG_InitializationManager.class).getList()) {
078                Element e = new Element("LogixNG").addContent(logixNG.getSystemName());   // NOI18N
079                elemInitializationTable.addContent(e);
080            }
081            logixNGs.addContent(elemInitializationTable);
082
083            // Store items on the clipboard
084            Element elemClipboard = new Element("Clipboard");  // NOI18N
085            Clipboard clipboard = tm.getClipboard();
086            if (clipboard.getFemaleSocket().isConnected()) {
087                Base rootObject = clipboard.getFemaleSocket().getConnectedSocket().getObject();
088                try {
089                    Element e = jmri.configurexml.ConfigXmlManager.elementFromObject(rootObject);
090                    if (e != null) {
091                        elemClipboard.addContent(e);
092                    }
093                } catch (Exception e) {
094                    log.error("Error storing action: {}", e, e);
095                }
096            }
097            logixNGs.addContent(elemClipboard);
098        }
099        return hasData ? logixNGs : null;
100    }
101
102    /**
103     * Subclass provides implementation to create the correct top element,
104     * including the type information. Default implementation is to use the
105     * local class here.
106     *
107     * @param logixngs The top-level element being created
108     */
109    public void setStoreElementClass(Element logixngs) {
110        logixngs.setAttribute("class", this.getClass().getName());  // NOI18N
111    }
112
113    /**
114     * Create a LogixNG_Manager object of the correct class, then register and
115     * fill it.
116     *
117     * @param sharedLogixNG  Shared top level Element to unpack.
118     * @param perNodeLogixNG Per-node top level Element to unpack.
119     * @return true if successful
120     */
121    @Override
122    public boolean load(Element sharedLogixNG, Element perNodeLogixNG) {
123        // create the master object
124        replaceLogixNGManager();
125        // load individual sharedLogix
126        loadThreads(sharedLogixNG);
127        loadLogixNGs(sharedLogixNG);
128        loadInitializationTable(sharedLogixNG);
129        loadClipboard(sharedLogixNG);
130        return true;
131    }
132
133    /**
134     * Utility method to load the individual LogixNG objects. If there's no
135     * additional info needed for a specific logixng type, invoke this with the
136     * parent of the set of LogixNG elements.
137     *
138     * @param sharedLogixNG Element containing the LogixNG elements to load.
139     */
140    public void loadThreads(Element sharedLogixNG) {
141        List<Element> threads = sharedLogixNG.getChildren("Thread");  // NOI18N
142        log.debug("Found {} threads", threads.size() );  // NOI18N
143
144        for (int i = 0; i < threads.size(); i++) {
145
146            Element threadElement = threads.get(i);
147
148            int threadId = Integer.parseInt(threadElement.getChild("id").getTextTrim());
149            String threadName = threadElement.getChild("name").getTextTrim();
150
151            log.debug("create thread: {}, {}", Integer.toString(threadId), threadName);  // NOI18N
152            LogixNG_Thread.createNewThread(threadId, threadName);
153        }
154    }
155
156    /**
157     * Utility method to load the individual LogixNG objects. If there's no
158     * additional info needed for a specific logixng type, invoke this with the
159     * parent of the set of LogixNG elements.
160     *
161     * @param sharedLogixNG Element containing the LogixNG elements to load.
162     */
163    public void loadLogixNGs(Element sharedLogixNG) {
164        List<Element> logixNGList = sharedLogixNG.getChildren("LogixNG");  // NOI18N
165        log.debug("Found {} logixngs", logixNGList.size() );  // NOI18N
166        LogixNG_Manager tm = InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class);
167
168        for (int i = 0; i < logixNGList.size(); i++) {
169
170            Element logixNG_Element = logixNGList.get(i);
171
172            String sysName = getSystemName(logixNG_Element);
173            if (sysName == null) {
174                log.warn("unexpected null in systemName {}", logixNG_Element);  // NOI18N
175                break;
176            }
177
178            String userName = getUserName(logixNG_Element);
179
180            String enabled = "";
181            if (logixNGList.get(i).getAttribute("enabled") != null) {  // NOI18N
182                enabled = logixNG_Element.getAttribute("enabled").getValue();  // NOI18N
183            }
184            boolean inline = false;
185            if (logixNGList.get(i).getAttribute("inline") != null) {  // NOI18N
186                inline = "yes".equals(logixNG_Element.getAttribute("inline").getValue());  // NOI18N
187            }
188            log.debug("create logixng: ({})({})", sysName, (userName == null ? "<null>" : userName) );  // NOI18N
189
190            // Create a new LogixNG but don't setup the initial tree.
191            DefaultLogixNG logixNG = (DefaultLogixNG)tm.createLogixNG(sysName, userName, inline);
192            if (logixNG != null) {
193                // load common part
194                loadCommon(logixNG, logixNGList.get(i));
195
196                // set enabled/disabled if attribute was present
197                if ((enabled != null) && (!enabled.equals(""))) {
198                    if (enabled.equals("yes")) {  // NOI18N
199                        logixNG.setEnabled(true);
200                    } else if (enabled.equals("no")) {  // NOI18N
201                        logixNG.setEnabled(false);
202                    }
203                }
204
205                List<Element> conditionalNGList =
206                        logixNG_Element.getChild("ConditionalNGs").getChildren();  // NOI18N
207
208                for (int j = 0; j < conditionalNGList.size(); j++) {
209
210                    Element systemNameElement = conditionalNGList.get(j);
211                    String systemName = null;
212                    if (systemNameElement != null) {
213                        systemName = systemNameElement.getTextTrim();
214                    }
215                    logixNG.setConditionalNG_SystemName(j, systemName);
216                }
217            }
218        }
219    }
220
221    public void loadInitializationTable(Element sharedLogixNG) {
222        LogixNG_Manager tm =
223                InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class);
224
225        LogixNG_InitializationManager initializationManager =
226                InstanceManager.getDefault(LogixNG_InitializationManager.class);
227
228        List<Element> initTableList = sharedLogixNG.getChildren("InitializationTable");  // NOI18N
229        if (initTableList.isEmpty()) return;
230        List<Element> logixNGList = initTableList.get(0).getChildren();
231        if (logixNGList.isEmpty()) return;
232        for (Element e : logixNGList) {
233            LogixNG logixNG = tm.getBySystemName(e.getTextTrim());
234            if (logixNG != null) {
235                initializationManager.add(logixNG);
236            } else {
237                log.warn("LogixNG '{}' cannot be found", e.getTextTrim());
238            }
239        }
240    }
241
242    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST",
243        justification="Error string generated by DefaultClipboard#replaceClipboardItems ")
244    public void loadClipboard(Element sharedLogixNG) {
245        List<Element> clipboardList = sharedLogixNG.getChildren("Clipboard");  // NOI18N
246        if (clipboardList.isEmpty()) return;
247        List<Element> clipboardSubList = clipboardList.get(0).getChildren();
248        if (clipboardSubList.isEmpty()) return;
249
250        String className = clipboardSubList.get(0).getAttribute("class").getValue();
251//        log.error("className: " + className);
252
253        Class<?> clazz;
254        try {
255            clazz = Class.forName(className);
256        } catch (ClassNotFoundException ex) {
257            log.error("cannot load class {}", className, ex);
258            return;
259        }
260
261        Constructor<?> c;
262        try {
263            c = clazz.getConstructor();
264        } catch (NoSuchMethodException | SecurityException ex) {
265            log.error("cannot create constructor", ex);
266            return;
267        }
268
269        try {
270            Object o = c.newInstance();
271
272            if (o == null) {
273                log.error("class is null");
274                return;
275            }
276            if (! (o instanceof ClipboardManyXml)) {
277                log.error("class has wrong type: {}", o.getClass().getName());
278                return;
279            }
280
281            LogixNG_Manager tm = InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class);
282            ClipboardMany anyMany = ((ClipboardManyXml)o).loadItem(clipboardList.get(0));
283            List<String> errors = new ArrayList<>();
284            if (! ((DefaultClipboard)tm.getClipboard()).replaceClipboardItems(anyMany, errors)) {
285                for (String s : errors) log.error(s);
286            }
287        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
288            log.error("cannot create object", ex);
289        }
290    }
291
292    /**
293     * Replace the current LogixManager, if there is one, with one newly created
294     * during a load operation. This is skipped if they are of the same absolute
295     * type.
296     */
297    protected void replaceLogixNGManager() {
298        if (InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class).getClass().getName()
299                .equals(DefaultLogixNGManager.class.getName())) {
300            return;
301        }
302        // if old manager exists, remove it from configuration process
303        if (InstanceManager.getNullableDefault(jmri.jmrit.logixng.LogixNG_Manager.class) != null) {
304            ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
305            if (cmOD != null) {
306                cmOD.deregister(InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class));
307            }
308
309        }
310
311        ThreadingUtil.runOnGUI(() -> {
312            // register new one with InstanceManager
313            DefaultLogixNGManager pManager = DefaultLogixNGManager.instance();
314            InstanceManager.store(pManager, LogixNG_Manager.class);
315            // register new one for configuration
316            ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
317            if (cmOD != null) {
318                cmOD.registerConfig(pManager, jmri.Manager.LOGIXNGS);
319            }
320        });
321    }
322
323    @Override
324    public int loadOrder() {
325        return InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class).getXMLOrder();
326    }
327
328    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultLogixNGManagerXml.class);
329}