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}