001package jmri.jmrit.logixng.implementation.configurexml;
002
003import java.lang.reflect.Constructor;
004import java.lang.reflect.InvocationTargetException;
005import java.util.ArrayList;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009
010import jmri.ConfigureManager;
011import jmri.InstanceManager;
012import jmri.configurexml.JmriConfigureXmlException;
013import jmri.jmrit.logixng.*;
014import jmri.jmrit.logixng.implementation.DefaultDigitalExpressionManager;
015import jmri.jmrit.logixng.implementation.DefaultMaleDigitalExpressionSocket;
016import jmri.managers.configurexml.AbstractNamedBeanManagerConfigXML;
017import jmri.util.ThreadingUtil;
018
019import org.jdom2.Element;
020
021/**
022 * Provides the functionality for configuring ExpressionManagers
023 *
024 * @author Dave Duchamp Copyright (c) 2007
025 * @author Daniel Bergqvist Copyright (c) 2018
026 */
027public class DefaultDigitalExpressionManagerXml extends AbstractManagerXml {
028
029    private final Map<String, Class<?>> xmlClasses = new HashMap<>();
030
031    public DefaultDigitalExpressionManagerXml() {
032    }
033
034    /**
035     * Default implementation for storing the contents of a LogixManager
036     *
037     * @param o Object to store, of type LogixManager
038     * @return Element containing the complete info
039     */
040    @Override
041    public Element store(Object o) {
042        Element expressions = new Element("LogixNGDigitalExpressions");
043        setStoreElementClass(expressions);
044        DigitalExpressionManager tm = (DigitalExpressionManager) o;
045        if (tm != null) {
046            if (tm.getNamedBeanSet().isEmpty()) return null;
047            for (MaleDigitalExpressionSocket expression : tm.getNamedBeanSet()) {
048                log.debug("action system name is {}", expression.getSystemName() );  // NOI18N
049                try {
050                    List<Element> elements = new ArrayList<>();
051                    // The male socket may be embedded in other male sockets
052                    MaleDigitalExpressionSocket a = expression;
053                    elements.add(storeMaleSocket(a));
054                    while (!(a instanceof DefaultMaleDigitalExpressionSocket)) {
055                        a = (MaleDigitalExpressionSocket) a.getObject();
056                    }
057                    Element e = jmri.configurexml.ConfigXmlManager.elementFromObject(a.getObject());
058                    if (e != null) {
059                        for (Element ee : elements) e.addContent(ee);
060                        expressions.addContent(e);
061                    } else {
062                        throw new RuntimeException("Cannot load xml configurator for " + a.getObject().getClass().getName());
063                    }
064                } catch (RuntimeException e) {
065                    log.error("Error storing action: {}", e, e);
066                }
067            }
068        }
069        return (expressions);
070    }
071
072    /**
073     * Subclass provides implementation to create the correct top element,
074     * including the type information. Default implementation is to use the
075     * local class here.
076     *
077     * @param expressions The top-level element being created
078     */
079    public void setStoreElementClass(Element expressions) {
080        expressions.setAttribute("class", this.getClass().getName());  // NOI18N
081    }
082
083    /**
084     * Create a DigitalExpressionManager object of the correct class, then
085     * register and fill it.
086     *
087     * @param sharedExpression  Shared top level Element to unpack.
088     * @param perNodeExpression Per-node top level Element to unpack.
089     * @return true if successful
090     */
091    @Override
092    public boolean load(Element sharedExpression, Element perNodeExpression) {
093        // create the master object
094        replaceExpressionManager();
095        // load individual sharedLogix
096        loadExpressions(sharedExpression);
097        return true;
098    }
099
100    /**
101     * Utility method to load the individual Logix objects. If there's no
102     * additional info needed for a specific logix type, invoke this with the
103     * parent of the set of Logix elements.
104     *
105     * @param expressions Element containing the Logix elements to load.
106     */
107    public void loadExpressions(Element expressions) {
108
109        List<Element> expressionList = expressions.getChildren();  // NOI18N
110        log.debug("Found {} expressions", expressionList.size());  // NOI18N
111//        DigitalExpressionManager tm = InstanceManager.getDefault(jmri.jmrit.logixng.DigitalExpressionManager.class);
112
113        for (int i = 0; i < expressionList.size(); i++) {
114
115            String className = expressionList.get(i).getAttribute("class").getValue();
116//            log.warn("className: " + className);
117
118            Class<?> clazz = xmlClasses.get(className);
119
120            if (clazz == null) {
121                try {
122                    className = jmri.configurexml.ConfigXmlManager.currentClassName(className);
123                    clazz = Class.forName(className);
124                    xmlClasses.put(className, clazz);
125                } catch (ClassNotFoundException ex) {
126                    log.error("cannot load class {}", className, ex);
127                }
128            }
129
130            if (clazz != null) {
131                Constructor<?> c = null;
132                try {
133                    c = clazz.getConstructor();
134                } catch (NoSuchMethodException | SecurityException ex) {
135                    log.error("cannot create constructor", ex);
136                }
137
138                if (c != null) {
139                    try {
140                        AbstractNamedBeanManagerConfigXML o = (AbstractNamedBeanManagerConfigXML)c.newInstance();
141
142                        MaleSocket oldLastItem = InstanceManager.getDefault(DigitalExpressionManager.class).getLastRegisteredMaleSocket();
143                        o.load(expressionList.get(i), null);
144
145                        // Load male socket data if a new bean has been registered
146                        MaleSocket newLastItem = InstanceManager.getDefault(DigitalExpressionManager.class).getLastRegisteredMaleSocket();
147                        if (newLastItem != oldLastItem) loadMaleSocket(expressionList.get(i), newLastItem);
148                        else throw new RuntimeException("No new bean has been added. This class: "+getClass().getName()+", new class: "+className);
149                    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
150                        log.error("cannot create object", ex);
151                    } catch (JmriConfigureXmlException ex) {
152                        log.error("cannot load action", ex);
153                    }
154                }
155            }
156        }
157    }
158
159    /**
160     * Replace the current DigitalExpressionManager, if there is one, with one newly created
161     * during a load operation. This is skipped if they are of the same absolute
162     * type.
163     */
164    protected void replaceExpressionManager() {
165        if (InstanceManager.getDefault(jmri.jmrit.logixng.DigitalExpressionManager.class).getClass().getName()
166                .equals(DefaultDigitalExpressionManager.class.getName())) {
167            return;
168        }
169        // if old manager exists, remove it from configuration process
170        if (InstanceManager.getNullableDefault(jmri.jmrit.logixng.DigitalExpressionManager.class) != null) {
171            ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
172            if (cmOD != null) {
173                cmOD.deregister(InstanceManager.getDefault(jmri.jmrit.logixng.DigitalExpressionManager.class));
174            }
175
176        }
177
178
179        ThreadingUtil.runOnGUI(() -> {
180            // register new one with InstanceManager
181            DefaultDigitalExpressionManager pManager = DefaultDigitalExpressionManager.instance();
182            InstanceManager.store(pManager, DigitalExpressionManager.class);
183            // register new one for configuration
184            ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
185            if (cmOD != null) {
186                cmOD.registerConfig(pManager, jmri.Manager.LOGIXNG_DIGITAL_EXPRESSIONS);
187            }
188        });
189    }
190
191    @Override
192    public int loadOrder() {
193        return InstanceManager.getDefault(jmri.jmrit.logixng.DigitalExpressionManager.class).getXMLOrder();
194    }
195
196    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultDigitalExpressionManagerXml.class);
197}