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