001package jmri.jmrit.logixng.implementation; 002 003import java.beans.*; 004import java.io.PrintWriter; 005import java.util.*; 006 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.jmrit.logixng.*; 011 012import org.apache.commons.lang3.mutable.MutableInt; 013 014/** 015 * Abstract female socket. 016 * 017 * @author Daniel Bergqvist 2019 018 */ 019public abstract class AbstractFemaleSocket implements FemaleSocket { 020 021 private Base _parent; 022 protected final FemaleSocketListener _listener; 023 private MaleSocket _socket = null; 024 private String _name = null; 025 private boolean _listenersAreRegistered = false; 026 boolean _enableListeners = true; 027 028 029 public AbstractFemaleSocket(Base parent, FemaleSocketListener listener, String name) { 030 if (!validateName(name)) { 031 throw new IllegalArgumentException("the name is not valid: " + name); 032 } 033 if (listener == null) throw new IllegalArgumentException("FemaleSocketListener is null"); 034 _parent = parent; 035 _listener = listener; 036 _name = name; 037 } 038 039 /** {@inheritDoc} */ 040 @Override 041 public void setEnableListeners(boolean enable) { 042 _enableListeners = enable; 043 } 044 045 /** {@inheritDoc} */ 046 @Override 047 public boolean getEnableListeners() { 048 return _enableListeners; 049 } 050 051 /** {@inheritDoc} */ 052 @Override 053 public Base getParent() { 054 return _parent; 055 } 056 057 /** {@inheritDoc} */ 058 @Override 059 public void setParent(@Nonnull Base parent) { 060 _parent = parent; 061 } 062 063 /** {@inheritDoc} */ 064 @Override 065 public boolean setParentForAllChildren(List<String> errors) { 066 if (isConnected()) { 067 getConnectedSocket().setParent(this); 068 return getConnectedSocket().setParentForAllChildren(errors); 069 } 070 return true; 071 } 072 073 /** {@inheritDoc} */ 074 @Override 075 public void connect(MaleSocket socket) throws SocketAlreadyConnectedException { 076 if (socket == null) { 077 throw new NullPointerException("socket cannot be null"); 078 } 079 080 if (_listenersAreRegistered) { 081 throw new UnsupportedOperationException("A socket must not be connected when listeners are registered"); 082 } 083 084 if (isConnected()) { 085 throw new SocketAlreadyConnectedException("Socket is already connected"); 086 } 087 088 if (!isCompatible(socket)) { 089 throw new IllegalArgumentException("Socket "+socket.getClass().getName()+" is not compatible with "+this.getClass().getName()); 090// throw new IllegalArgumentException("Socket "+socket.getClass().getName()+" is not compatible with "+this.getClass().getName()+". Socket.getObject: "+socket.getObject().getClass().getName()); 091 } 092 093 _socket = socket; 094 _socket.setParent(this); 095 _listener.connected(this); 096 pcs.firePropertyChange(new PropertyChangeEvent(this, Base.PROPERTY_SOCKET_CONNECTED, null, _socket)); 097// pcs.firePropertyChange(Base.PROPERTY_SOCKET_CONNECTED, null, _socket); 098 } 099 100 /** {@inheritDoc} */ 101 @Override 102 public void disconnect() { 103 MaleSocket maleSocket = _socket; 104 105 if (_socket == null) { 106 return; 107 } 108 109 if (_listenersAreRegistered) { 110 throw new UnsupportedOperationException("A socket must not be disconnected when listeners are registered"); 111 } 112 113 _socket.setParent(null); 114 _socket = null; 115 _listener.disconnected(this); 116 pcs.firePropertyChange(new PropertyChangeEvent(this, Base.PROPERTY_SOCKET_DISCONNECTED, maleSocket, null)); 117// pcs.firePropertyChange(Base.PROPERTY_SOCKET_DISCONNECTED, null, _socket); 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public MaleSocket getConnectedSocket() { 123 return _socket; 124 } 125 126 /** {@inheritDoc} */ 127 @Override 128 public boolean isConnected() { 129 return _socket != null; 130 } 131 132 /** {@inheritDoc} */ 133 @Override 134 public final boolean validateName(String name, boolean ignoreDuplicateErrors) { 135 // Empty name is not allowed 136 if (name.isEmpty()) return false; 137 138 // The name must start with a letter 139 if (!Character.isLetter(name.charAt(0))) return false; 140 141 // The name must consist of letters, digits or underscore 142 for (int i=0; i < name.length(); i++) { 143 if (!Character.isLetterOrDigit(name.charAt(i)) && (name.charAt(i) != '_')) { 144 return false; 145 } 146 } 147 148 if (!ignoreDuplicateErrors && (_parent != null)) { 149 // Check that no other female socket of the parent has the same name 150 for (int i=0; i < _parent.getChildCount(); i++) { 151 FemaleSocket child = _parent.getChild(i); 152 if ((child != this) && name.equals(child.getName())) return false; 153 } 154 } 155 156 // The name is valid 157 return true; 158 } 159 160 /** {@inheritDoc} */ 161 @Override 162 public void setName(String name, boolean ignoreDuplicateErrors) { 163 if (!validateName(name, ignoreDuplicateErrors)) { 164 throw new IllegalArgumentException("the name is not valid: " + name); 165 } 166 _name = name; 167 _listener.socketNameChanged(this); 168 } 169 170 /** {@inheritDoc} */ 171 @Override 172 public String getName() { 173 return _name; 174 } 175 176 abstract public void disposeMe(); 177 178 /** {@inheritDoc} */ 179 @Override 180 public final void dispose() { 181 if (_listenersAreRegistered) { 182 throw new UnsupportedOperationException("This is not currently supported"); 183 } 184 185 if (isConnected()) { 186 MaleSocket aSocket = getConnectedSocket(); 187 disconnect(); 188 aSocket.dispose(); 189 } 190 disposeMe(); 191 } 192 193 /** 194 * Register listeners if this object needs that. 195 * <P> 196 * Important: This method may be called more than once. Methods overriding 197 * this method must ensure that listeners are not registered more than once. 198 */ 199 protected void registerListenersForThisClass() { 200 // Do nothing 201 } 202 203 /** 204 * Unregister listeners if this object needs that. 205 * <P> 206 * Important: This method may be called more than once. Methods overriding 207 * this method must ensure that listeners are not unregistered more than once. 208 */ 209 protected void unregisterListenersForThisClass() { 210 // Do nothing 211 } 212 213 /** 214 * Register listeners if this object needs that. 215 */ 216 @Override 217 public void registerListeners() { 218 if (!_enableListeners) return; 219 220 _listenersAreRegistered = true; 221 registerListenersForThisClass(); 222 if (isConnected()) { 223 getConnectedSocket().registerListeners(); 224 } 225 } 226 227 /** 228 * Register listeners if this object needs that. 229 */ 230 @Override 231 public void unregisterListeners() { 232 if (!_enableListeners) return; 233 234 unregisterListenersForThisClass(); 235 if (isConnected()) { 236 getConnectedSocket().unregisterListeners(); 237 } 238 _listenersAreRegistered = false; 239 } 240 241 /** {@inheritDoc} */ 242 @Override 243 public final boolean isActive() { 244 return isEnabled() && ((_parent == null) || _parent.isActive()); 245 } 246 247 /** {@inheritDoc} */ 248 @Override 249 public Category getCategory() { 250 throw new UnsupportedOperationException("Not supported."); 251 } 252 253 /** {@inheritDoc} */ 254 @Override 255 public FemaleSocket getChild(int index) { 256 throw new UnsupportedOperationException("Not supported."); 257 } 258 259 /** {@inheritDoc} */ 260 @Override 261 public int getChildCount() { 262 throw new UnsupportedOperationException("Not supported."); 263 } 264 265 /** {@inheritDoc} */ 266 @Override 267 public String getUserName() { 268 throw new UnsupportedOperationException("Not supported."); 269 } 270 271 /** {@inheritDoc} */ 272 @Override 273 public void setUserName(String s) throws NamedBean.BadUserNameException { 274 throw new UnsupportedOperationException("Not supported."); 275 } 276 277 /** {@inheritDoc} */ 278 @Override 279 public String getComment() { 280 throw new UnsupportedOperationException("Not supported."); 281 } 282 283 /** {@inheritDoc} */ 284 @Override 285 public void setComment(String s) { 286 throw new UnsupportedOperationException("Not supported."); 287 } 288 289 /** {@inheritDoc} */ 290 @Override 291 public String getSystemName() { 292 return getParent().getSystemName(); 293 } 294 295 /** {@inheritDoc} */ 296 @Override 297 public final ConditionalNG getConditionalNG() { 298 if (_parent == null) return null; 299 return _parent.getConditionalNG(); 300 } 301 302 /** {@inheritDoc} */ 303 @Override 304 public final LogixNG getLogixNG() { 305 if (_parent == null) return null; 306 return _parent.getLogixNG(); 307 } 308 309 /** {@inheritDoc} */ 310 @Override 311 public final Base getRoot() { 312 if (_parent == null) return null; 313 return _parent.getRoot(); 314 } 315 316 protected void printTreeRow( 317 PrintTreeSettings settings, 318 Locale locale, 319 PrintWriter writer, 320 String currentIndent, 321 MutableInt lineNumber) { 322 323 if (settings._printLineNumbers) { 324 writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1))); 325 } 326 writer.append(currentIndent); 327 writer.append(getLongDescription(locale)); 328 writer.println(); 329 } 330 331 /** {@inheritDoc} */ 332 @Override 333 public void printTree( 334 PrintTreeSettings settings, 335 PrintWriter writer, 336 String indent, 337 MutableInt lineNumber) { 338 339 throw new UnsupportedOperationException("Not supported."); 340 } 341 342 /** {@inheritDoc} */ 343 @Override 344 public void printTree( 345 PrintTreeSettings settings, 346 Locale locale, 347 PrintWriter writer, 348 String indent, 349 MutableInt lineNumber) { 350 351 throw new UnsupportedOperationException("Not supported."); 352 } 353 354 /** {@inheritDoc} */ 355 @Override 356 public void printTree( 357 PrintTreeSettings settings, 358 Locale locale, 359 PrintWriter writer, 360 String indent, 361 String currentIndent, 362 MutableInt lineNumber) { 363 364 printTreeRow(settings, locale, writer, currentIndent, lineNumber); 365 366 if (isConnected()) { 367 getConnectedSocket().printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber); 368 } else { 369 if (settings._printLineNumbers) { 370 writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1))); 371 } 372 writer.append(currentIndent); 373 writer.append(indent); 374 if (settings._printNotConnectedSockets) writer.append("Socket not connected"); 375 writer.println(); 376 } 377 } 378 379 /** {@inheritDoc} */ 380 @Override 381 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT", 382 justification="Specific log message format") 383 public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 384 log.debug("** {} :: {}", level, this.getLongDescription()); 385 level++; 386 387 if (isConnected()) { 388 getConnectedSocket().getUsageTree(level, bean, report, cdl); 389 } 390 } 391 392 /** {@inheritDoc} */ 393 @Override 394 public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 395 } 396 397 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 398 399 @Override 400 public void addPropertyChangeListener(PropertyChangeListener listener) { 401 pcs.addPropertyChangeListener(listener); 402 } 403 404 @Override 405 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { 406 pcs.addPropertyChangeListener(propertyName, listener); 407 } 408 409 @Override 410 public PropertyChangeListener[] getPropertyChangeListeners() { 411 return pcs.getPropertyChangeListeners(); 412 } 413 414 @Override 415 public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { 416 return pcs.getPropertyChangeListeners(propertyName); 417 } 418 419 @Override 420 public void removePropertyChangeListener(PropertyChangeListener listener) { 421 pcs.removePropertyChangeListener(listener); 422 } 423 424 @Override 425 public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { 426 pcs.removePropertyChangeListener(propertyName, listener); 427 } 428 429 @Override 430 public void addPropertyChangeListener(PropertyChangeListener listener, String name, String listenerRef) { 431 throw new UnsupportedOperationException("Not supported"); 432 } 433 434 @Override 435 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener, String name, String listenerRef) { 436 throw new UnsupportedOperationException("Not supported"); 437 } 438 439 @Override 440 public void updateListenerRef(PropertyChangeListener l, String newName) { 441 throw new UnsupportedOperationException("Not supported"); 442 } 443 444 @Override 445 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 446 throw new UnsupportedOperationException("Not supported"); 447 } 448 449 @Override 450 public String getListenerRef(PropertyChangeListener l) { 451 throw new UnsupportedOperationException("Not supported"); 452 } 453 454 @Override 455 public ArrayList<String> getListenerRefs() { 456 throw new UnsupportedOperationException("Not supported"); 457 } 458 459 @Override 460 public int getNumPropertyChangeListeners() { 461 return pcs.getPropertyChangeListeners().length; 462 } 463 464 @Override 465 public PropertyChangeListener[] getPropertyChangeListenersByReference(String name) { 466 throw new UnsupportedOperationException("Not supported"); 467 } 468 469 /** 470 * Do something on every item in the sub tree of this item. 471 * @param r the action to do on all items. 472 */ 473 @Override 474 public void forEntireTree(RunnableWithBase r) { 475 r.run(this); 476 if (isConnected()) getConnectedSocket().forEntireTree(r); 477 } 478 479 /** 480 * Do something on every item in the sub tree of this item. 481 * @param r the action to do on all items. 482 * @throws Exception if an exception occurs 483 */ 484 @Override 485 public void forEntireTreeWithException(RunnableWithBaseThrowException r) throws Exception { 486 r.run(this); 487 if (isConnected()) getConnectedSocket().forEntireTreeWithException(r); 488 } 489 490 @Override 491 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) { 492 throw new UnsupportedOperationException("Not supported"); 493 } 494 495 @Override 496 public Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 497 throw new UnsupportedOperationException("Not supported"); 498 } 499 500 /** {@inheritDoc} */ 501 @Override 502 public void getListenerRefsIncludingChildren(List<String> list) { 503 if (isConnected()) { 504 getConnectedSocket().getListenerRefsIncludingChildren(list); 505 } 506 } 507 508 @Override 509 public boolean hasChild(@Nonnull Base b) { 510 return isConnected() && getConnectedSocket() == b; 511 } 512 513 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractFemaleSocket.class); 514 515}