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.DefaultMaleStringActionSocket; 012import jmri.jmrit.logixng.implementation.DefaultStringActionManager; 013import jmri.managers.configurexml.AbstractNamedBeanManagerConfigXML; 014import jmri.util.ThreadingUtil; 015 016import org.jdom2.Element; 017 018/** 019 * Provides the functionality for configuring ActionManagers 020 * 021 * @author Dave Duchamp Copyright (c) 2007 022 * @author Daniel Bergqvist Copyright (c) 2018 023 */ 024public class DefaultStringActionManagerXml extends AbstractManagerXml { 025 026 private final Map<String, Class<?>> xmlClasses = new HashMap<>(); 027 028 public DefaultStringActionManagerXml() { 029 } 030 031 /** 032 * Default implementation for storing the contents of a StringActionManager 033 * 034 * @param o Object to store, of type StringActionManager 035 * @return Element containing the complete info 036 */ 037 @Override 038 public Element store(Object o) { 039 Element actions = new Element("LogixNGStringActions"); 040 setStoreElementClass(actions); 041 StringActionManager tm = (StringActionManager) o; 042 if (tm != null) { 043 if (tm.getNamedBeanSet().isEmpty()) return null; 044 for (MaleStringActionSocket action : tm.getNamedBeanSet()) { 045 log.debug("action system name is {}", action.getSystemName()); // NOI18N 046// log.error("action system name is " + action.getSystemName() + ", " + action.getLongDescription()); // NOI18N 047 try { 048 List<Element> elements = new ArrayList<>(); 049 // The male socket may be embedded in other male sockets 050 MaleStringActionSocket a = action; 051 elements.add(storeMaleSocket(a)); 052 while (!(a instanceof DefaultMaleStringActionSocket)) { 053 a = (MaleStringActionSocket) 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(a)); 059 actions.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 (actions); 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 actions The top-level element being created 077 */ 078 public void setStoreElementClass(Element actions) { 079 actions.setAttribute("class", this.getClass().getName()); // NOI18N 080 } 081 082 /** 083 * Create a StringActionManager object of the correct class, then register 084 * and fill it. 085 * 086 * @param sharedAction Shared top level Element to unpack. 087 * @param perNodeAction Per-node top level Element to unpack. 088 * @return true if successful 089 */ 090 @Override 091 public boolean load(Element sharedAction, Element perNodeAction) { 092 // create the master object 093 replaceActionManager(); 094 // load individual sharedAction 095 loadActions(sharedAction); 096 return true; 097 } 098 099 /** 100 * Utility method to load the individual StringActionBean objects. If 101 * there's no additional info needed for a specific action type, invoke 102 * this with the parent of the set of StringActionBean elements. 103 * 104 * @param actions Element containing the StringActionBean elements to load. 105 */ 106 public void loadActions(Element actions) { 107 108 List<Element> actionList = actions.getChildren(); // NOI18N 109 log.debug("Found {} actions", actionList.size() ); // NOI18N 110 111 for (int i = 0; i < actionList.size(); i++) { 112 113 String className = actionList.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(StringActionManager.class).getLastRegisteredMaleSocket(); 141 o.load(actionList.get(i), null); 142 143 // Load male socket data if a new bean has been registered 144 MaleSocket newLastItem = InstanceManager.getDefault(StringActionManager.class).getLastRegisteredMaleSocket(); 145 if (newLastItem != oldLastItem) loadMaleSocket(actionList.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 StringActionManager, if there is one, with one newly 159 * created during a load operation. This is skipped if they are of the same absolute 160 * type. 161 */ 162 protected void replaceActionManager() { 163 if (InstanceManager.getDefault(jmri.jmrit.logixng.StringActionManager.class).getClass().getName() 164 .equals(DefaultStringActionManager.class.getName())) { 165 return; 166 } 167 // if old manager exists, remove it from configuration process 168 if (InstanceManager.getNullableDefault(jmri.jmrit.logixng.StringActionManager.class) != null) { 169 ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class); 170 if (cmOD != null) { 171 cmOD.deregister(InstanceManager.getDefault(jmri.jmrit.logixng.StringActionManager.class)); 172 } 173 174 } 175 176 ThreadingUtil.runOnGUI(() -> { 177 // register new one with InstanceManager 178 DefaultStringActionManager pManager = DefaultStringActionManager.instance(); 179 InstanceManager.store(pManager, StringActionManager.class); 180 // register new one for configuration 181 ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class); 182 if (cmOD != null) { 183 cmOD.registerConfig(pManager, jmri.Manager.LOGIXNG_STRING_ACTIONS); 184 } 185 }); 186 } 187 188 @Override 189 public int loadOrder() { 190 return InstanceManager.getDefault(jmri.jmrit.logixng.StringActionManager.class).getXMLOrder(); 191 } 192 193 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultStringActionManagerXml.class); 194}