001package jmri.jmrix.ieee802154.xbee.swing.nodeconfig; 002 003import java.awt.BorderLayout; 004import java.awt.event.ActionEvent; 005import javax.swing.BorderFactory; 006import javax.swing.BoxLayout; 007import javax.swing.JComboBox; 008import javax.swing.JComponent; 009import javax.swing.JPanel; 010import javax.swing.JScrollPane; 011import jmri.ConfigureManager; 012import jmri.jmrix.AbstractStreamConnectionConfig; 013import jmri.jmrix.StreamConnectionConfig; 014import jmri.jmrix.ConnectionConfigManager; 015import jmri.jmrix.JmrixConfigPane; 016import jmri.InstanceManager; 017import jmri.swing.JTitledSeparator; 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020import jmri.jmrix.ieee802154.xbee.XBeeNode; 021 022/** 023 * Provide GUI to configure communications links. 024 * <p> 025 * This is really just a catalog of connections to classes within the systems. 026 * Reflection is used to reduce coupling at load time. 027 * <p> 028 * Objects of this class are based on an underlying ConnectionConfig 029 * implementation, which in turn is obtained from the InstanceManager. Those 030 * must be created at load time by the ConfigXml process, or in some Application 031 * class. 032 * <p> 033 * The classes referenced are the specific subclasses of 034 * {@link jmri.jmrix.StreamConnectionConfig} which provides the methods providing data 035 * to the configuration GUI, and responding to its changes. 036 * 037 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2004, 2010 038 */ 039public class StreamConfigPane extends JmrixConfigPane { 040 041 // initialize logging 042 private final static Logger log = LoggerFactory.getLogger(StreamConfigPane.class); 043 044 private XBeeNode confNode = null; 045 046 /* 047 * Create panel is seperated off from the instance and synchronized, so that only 048 * one connection can be configured at once, this prevents multiple threads from 049 * trying to create the same panel at the same time. 050 */ 051 052 /** 053 * Create a new connection configuration panel. 054 * 055 * @param node the XBee node associated with the connection. 056 * 057 * @return the panel for the requested connection or for a new connection if 058 * index did not match an existing connection configuration 059 */ 060 public static synchronized StreamConfigPane createPanel(XBeeNode node) { 061 StreamConnectionConfig c = node.getConnectionConfig(); 062 return createPanel(c); 063 } 064 065 /** 066 * Create a new configuration panel for the given connection. 067 * 068 * @param c the connection; if null, the panel is ready for a new connection 069 * @return the new panel 070 */ 071 public static synchronized StreamConfigPane createPanel(StreamConnectionConfig c) { 072 StreamConfigPane pane = new StreamConfigPane(c); 073 if (c == null) { 074 pane.isDirty = true; 075 } 076 return pane; 077 } 078 079 /** 080 * Disposes of the underlying connection for a configuration pane. 081 * 082 * @param confPane the pane to dispose of 083 */ 084 public static void dispose(StreamConfigPane confPane) { 085 if (confPane == null) { 086 log.debug("no instance found therefore can not dispose of it!"); 087 return; 088 } 089 090 if (confPane.ccCurrent != null) { 091 try { 092 confPane.ccCurrent.dispose(); 093 } catch (RuntimeException ex) { 094 log.error("Error Occurred while disposing connection {}", ex.toString()); 095 } 096 } 097 ConfigureManager cmOD = InstanceManager.getNullableDefault(jmri.ConfigureManager.class); 098 if (cmOD != null) { 099 cmOD.deregister(confPane); 100 //cmOD.deregister(confPane.ccCurrent); 101 } 102 InstanceManager.getDefault(ConnectionConfigManager.class).remove(confPane.ccCurrent); 103 } 104 105 public void setXBeeNode(XBeeNode node){ 106 confNode = node; 107 } 108 109 private boolean isDirty = false; 110 111 final JComboBox<String> modeBox = new JComboBox<>(); 112 final JComboBox<String> manuBox = new JComboBox<>(); 113 114 final JPanel details = new JPanel(); 115 String[] classConnectionNameList; 116 StreamConnectionConfig[] classConnectionList; 117 final String[] manufactureNameList; 118 119 final jmri.UserPreferencesManager p = jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class); 120 121 StreamConnectionConfig ccCurrent = null; 122 123 /** 124 * Use "instance" to get one of these. That allows it to reconnect to 125 * existing information in an existing StreamConnectionConfig object. It's 126 * permitted to call this with a null argument, e.g. for when first 127 * configuring the system. 128 * @param original Connection configuration to use 129 */ 130 protected StreamConfigPane(StreamConnectionConfig original) { 131 ConnectionConfigManager manager = InstanceManager.getDefault(ConnectionConfigManager.class); 132 ccCurrent = original; 133 134 setLayout(new BorderLayout()); 135 this.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); 136 137 manuBox.addItem(NONE_SELECTED); 138 139 manufactureNameList = manager.getConnectionManufacturers(); 140 for (String manuName : manufactureNameList) { 141 if (original != null && original.getManufacturer() != null 142 && original.getManufacturer().equals(manuName)) { 143 manuBox.addItem(manuName); 144 manuBox.setSelectedItem(manuName); 145 } else { 146 manuBox.addItem(manuName); 147 } 148 } 149 manuBox.addActionListener((ActionEvent evt) -> updateComboConnection()); 150 151 // get the list of ConnectionConfig items into a selection box 152 classConnectionNameList = manager.getConnectionTypes((String) manuBox.getSelectedItem()); 153 classConnectionList = new jmri.jmrix.StreamConnectionConfig[classConnectionNameList.length + 1]; 154 modeBox.addItem(NONE_SELECTED); 155 //if (manuBox.getSelectedIndex() != 0) { 156 modeBox.setEnabled(true); 157 //} else { 158 modeBox.setSelectedIndex(0); 159 modeBox.setEnabled(false); 160 //} 161 int n = 1; 162 if (manuBox.getSelectedIndex() != 0) { 163 for (String className : classConnectionNameList) { 164 try { 165 StreamConnectionConfig config; 166 if (original != null && original.getClass().getName().equals(className)) { 167 config = original; 168 log.debug("matched existing config object"); 169 modeBox.addItem(config.name()); 170 modeBox.setSelectedItem(config.name()); 171 if (classConnectionNameList.length == 1) { 172 modeBox.setSelectedIndex(1); 173 } 174 } else { 175 Class<?> cl = Class.forName(className); 176 try { 177 config = (StreamConnectionConfig) cl.getDeclaredConstructor().newInstance(); 178 modeBox.addItem(config.name()); 179 } catch (ClassCastException cce) { 180 // the list may include non-StreamConnectinoConfig 181 // objects, so just ignore those. 182 continue; 183 } 184 } 185 classConnectionList[n++] = config; 186 } catch (NullPointerException e) { 187 log.error("Attempt to load {} failed.", className, e); 188 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | java.lang.reflect.InvocationTargetException e) { 189 log.debug("Attempt to load {} failed", className, e); 190 } 191 } 192 if ((modeBox.getSelectedIndex() == 0) && (p.getComboBoxLastSelection((String) manuBox.getSelectedItem()) != null)) { 193 modeBox.setSelectedItem(p.getComboBoxLastSelection((String) manuBox.getSelectedItem())); 194 } 195 } 196 modeBox.addActionListener((ActionEvent a) -> { 197 if (modeBox.getSelectedItem() != null) { 198 if (!(modeBox.getSelectedItem()).equals(NONE_SELECTED)) { 199 p.setComboBoxLastSelection((String) manuBox.getSelectedItem(), (String) modeBox.getSelectedItem()); 200 } 201 } 202 select(); 203 }); 204 JPanel manufacturerPanel = new JPanel(); 205 manufacturerPanel.add(manuBox); 206 JPanel connectionPanel = new JPanel(); 207 connectionPanel.add(modeBox); 208 JPanel initialPanel = new JPanel(); 209 initialPanel.setLayout(new BoxLayout(initialPanel, BoxLayout.Y_AXIS)); 210 initialPanel.add(new JTitledSeparator(Bundle.getMessage("SystemManufacturer"))); // NOI18N 211 initialPanel.add(manufacturerPanel); 212 initialPanel.add(new JTitledSeparator(Bundle.getMessage("SystemConnection"))); // NOI18N 213 initialPanel.add(connectionPanel); 214 add(initialPanel, BorderLayout.NORTH); 215 initialPanel.add(new JTitledSeparator(Bundle.getMessage("Settings"))); // NOI18N 216 JScrollPane scroll = new JScrollPane(details); 217 scroll.setBorder(BorderFactory.createEmptyBorder()); 218 add(scroll, BorderLayout.CENTER); 219 220 select(); // first time through, pretend we've selected a value 221 // to load the rest of the GUI 222 } 223 224 @Override 225 public void updateComboConnection() { 226 modeBox.removeAllItems(); 227 modeBox.addItem(NONE_SELECTED); 228 classConnectionNameList = InstanceManager.getDefault(ConnectionConfigManager.class).getConnectionTypes((String) manuBox.getSelectedItem()); 229 classConnectionList = new jmri.jmrix.StreamConnectionConfig[classConnectionNameList.length + 1]; 230 231 if (manuBox.getSelectedIndex() != 0) { 232 modeBox.setEnabled(true); 233 } else { 234 modeBox.setSelectedIndex(0); 235 modeBox.setEnabled(false); 236 } 237 238 int n = 1; 239 if (manuBox.getSelectedIndex() != 0) { 240 for (String classConnectionNameList1 : classConnectionNameList) { 241 try { 242 jmri.jmrix.StreamConnectionConfig config; 243 Class<?> cl = Class.forName(classConnectionNameList1); 244 config = (jmri.jmrix.StreamConnectionConfig) cl.getDeclaredConstructor().newInstance(); 245 modeBox.addItem(config.name()); 246 classConnectionList[n++] = config; 247 if (classConnectionNameList.length == 1) { 248 modeBox.setSelectedIndex(1); 249 } 250 } catch (ClassCastException cce) { 251 // the list may include non-StreamConnectinoConfig 252 // objects, so just ignore those. 253 log.trace("ignoring {} as invalid connection type", classConnectionNameList1); 254 } catch (NullPointerException | ClassNotFoundException | InstantiationException | 255 IllegalAccessException | NoSuchMethodException | java.lang.reflect.InvocationTargetException e) { 256 log.warn("Attempt to load {} failed", classConnectionNameList1, e); 257 } 258 } 259 if (p.getComboBoxLastSelection((String) manuBox.getSelectedItem()) != null) { 260 modeBox.setSelectedItem(p.getComboBoxLastSelection((String) manuBox.getSelectedItem())); 261 } 262 } else { 263 if (ccCurrent != null) { 264 ccCurrent.dispose(); 265 } 266 } 267 } 268 269 void select() { 270 StreamConnectionConfig old = this.ccCurrent; 271 int current = modeBox.getSelectedIndex(); 272 details.removeAll(); 273 // first choice is -no- protocol chosen 274 log.debug("new selection is {} {}", current, modeBox.getSelectedItem()); 275 if ((current != 0) && (current != -1)) { 276 if ((ccCurrent != null) && (ccCurrent != classConnectionList[current])) { 277 ccCurrent.dispose(); 278 } 279 ccCurrent = classConnectionList[current]; 280 classConnectionList[current].loadDetails(details); 281 classConnectionList[current].setManufacturer((String) manuBox.getSelectedItem()); 282 } else { 283 if (ccCurrent != null) { 284 ccCurrent.dispose(); 285 } 286 } 287 if (old != this.ccCurrent) { 288 // store the connection config with the node. 289 if(ccCurrent instanceof AbstractStreamConnectionConfig) { 290 confNode.setPortController((AbstractStreamConnectionConfig)ccCurrent); 291 } 292 } 293 294 validate(); 295 296 repaint(); 297 } 298 299 @Override 300 public String getConnectionName() { 301 int current = modeBox.getSelectedIndex(); 302 if (current == 0) { 303 return null; 304 } 305 return classConnectionList[current].getConnectionName(); 306 } 307 308 @Override 309 public String getCurrentManufacturerName() { 310 int current = modeBox.getSelectedIndex(); 311 if (current == 0) { 312 return NONE; 313 } 314 return classConnectionList[current].getManufacturer(); 315 } 316 317 @Override 318 public String getCurrentProtocolName() { 319 int current = modeBox.getSelectedIndex(); 320 if (current == 0) { 321 return NONE; 322 } 323 return classConnectionList[current].name(); 324 } 325 326 @Override 327 public String getCurrentProtocolInfo() { 328 int current = modeBox.getSelectedIndex(); 329 if (current == 0) { 330 return NONE; 331 } 332 return classConnectionList[current].getInfo(); 333 } 334 335 @Override 336 public StreamConnectionConfig getCurrentObject() { 337 int current = modeBox.getSelectedIndex(); 338 if (current != 0) { 339 return classConnectionList[current]; 340 } 341 return null; 342 } 343 344 @Override 345 public boolean getDisabled() { 346 int current = modeBox.getSelectedIndex(); 347 if (current == 0) { 348 return false; 349 } 350 return classConnectionList[current].getDisabled(); 351 } 352 353 @Override 354 public void setDisabled(boolean disabled) { 355 int current = modeBox.getSelectedIndex(); 356 if (current == 0) { 357 return; 358 } 359 classConnectionList[current].setDisabled(disabled); 360 } 361 362 @Override 363 public String getTabbedPreferencesTitle() { 364 String title = this.getConnectionName(); 365 if (title == null 366 && this.getCurrentProtocolName() != null 367 && !this.getCurrentProtocolName().equals(JmrixConfigPane.NONE)) { 368 title = this.getCurrentProtocolName(); 369 } 370 if (title != null && !this.getDisabled()) { 371 title = "(" + title + ")"; 372 } 373 return title; 374 } 375 376 @Override 377 public String getLabelKey() { 378 return null; 379 } 380 381 @Override 382 public JComponent getPreferencesComponent() { 383 return this; 384 } 385 386 @Override 387 public boolean isPersistant() { 388 return true; 389 } 390 391 @Override 392 public String getPreferencesTooltip() { 393 return this.getTabbedPreferencesTitle(); 394 } 395 396 @Override 397 public void savePreferences() { 398 // do nothing - the persistant manager will take care of this 399 } 400 401 @Override 402 public boolean isDirty() { 403 // avoid potentially expensive exrta test for isDirty 404 if (log.isDebugEnabled()) { 405 log.debug("Connection \"{}\" is {}.", 406 this.getConnectionName(), 407 (this.isDirty || ((this.ccCurrent != null) ? this.ccCurrent.isDirty() : true) ? "dirty" : "clean")); 408 } 409 return this.isDirty || ((this.ccCurrent != null) ? this.ccCurrent.isDirty() : true); 410 } 411 412 @Override 413 public boolean isRestartRequired() { 414 return (this.ccCurrent != null) ? this.ccCurrent.isRestartRequired() : this.isDirty(); 415 } 416 417 @Override 418 public boolean isPreferencesValid() { 419 return true; // no validity checking performed 420 } 421 422}