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