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