001package jmri.jmrit.logixng.implementation; 002 003import java.beans.*; 004import java.util.ArrayList; 005import java.util.List; 006 007import javax.annotation.Nonnull; 008import javax.annotation.OverridingMethodsMustInvokeSuper; 009 010import jmri.NamedBean; 011import jmri.jmrit.logixng.*; 012import jmri.managers.AbstractManager; 013 014/** 015 * Abstract partial implementation for the LogixNG action and expression managers. 016 * 017 * @param <E> the type of NamedBean supported by this manager 018 * 019 * @author Daniel Bergqvist 2020 020 */ 021public abstract class AbstractBaseManager<E extends NamedBean> extends AbstractManager<E> implements BaseManager<E> { 022 023 protected List<MaleSocketFactory<E>> _maleSocketFactories = new ArrayList<>(); 024 025 026 /** 027 * Inform all registered listeners of a vetoable change.If the propertyName 028 * is "CanDelete" ALL listeners with an interest in the bean will throw an 029 * exception, which is recorded returned back to the invoking method, so 030 * that it can be presented back to the user.However if a listener decides 031 * that the bean can not be deleted then it should throw an exception with 032 * a property name of "DoNotDelete", this is thrown back up to the user and 033 * the delete process should be aborted. 034 * 035 * @param p The programmatic name of the property that is to be changed. 036 * "CanDelete" will inquire with all listeners if the item can 037 * be deleted. "DoDelete" tells the listener to delete the item. 038 * @param old The old value of the property. 039 * @throws java.beans.PropertyVetoException If the recipients wishes the 040 * delete to be aborted (see above) 041 */ 042 @OverridingMethodsMustInvokeSuper 043 public void fireVetoableChange(String p, Object old) throws PropertyVetoException { 044 PropertyChangeEvent evt = new PropertyChangeEvent(this, p, old, null); 045 for (VetoableChangeListener vc : vetoableChangeSupport.getVetoableChangeListeners()) { 046 vc.vetoableChange(evt); 047 } 048 } 049 050 /** 051 * Cast the maleSocket to E 052 * This method is needed since SpotBugs @SuppressWarnings("unchecked") 053 * does not work for the cast: (E)socket. 054 * @param maleSocket the maleSocket to cast 055 * @return the maleSocket as E 056 */ 057 protected abstract E castBean(MaleSocket maleSocket); 058 059 /** {@inheritDoc} */ 060 @Override 061// @OverridingMethodsMustInvokeSuper 062 public final void deleteBean(@Nonnull E n, @Nonnull String property) throws PropertyVetoException { 063 this.deleteBean((MaleSocket)n, property); 064 } 065 066 /** {@inheritDoc} */ 067 @Override 068 @OverridingMethodsMustInvokeSuper 069// @SuppressWarnings("unchecked") // cast in "deregister((E)socket)" is nessesary and cannot be avoided 070 public void deleteBean(@Nonnull MaleSocket socket, @Nonnull String property) throws PropertyVetoException { 071 for (int i=0; i < socket.getChildCount(); i++) { 072 FemaleSocket child = socket.getChild(i); 073 if (child.isConnected()) { 074 MaleSocket maleSocket = child.getConnectedSocket(); 075 maleSocket.getManager().deleteBean(maleSocket, property); 076 } 077 } 078 079 // throws PropertyVetoException if vetoed 080 fireVetoableChange(property, socket); 081 if (property.equals("DoDelete")) { // NOI18N 082 deregister(castBean(socket)); 083 socket.dispose(); 084 } 085 } 086 087 /** {@inheritDoc} */ 088 @Override 089 @OverridingMethodsMustInvokeSuper 090 public void deregister(@Nonnull E s) { 091 // A LogixNG action or expression is contained in one or more male 092 // sockets. A male socket might be contained in another male socket. 093 // In some cases, it seems that the male socket used in this call is 094 // not the male socket that's registered in the manager. To resolve 095 // this, we search for the registered bean with the system name and 096 // then deregister the bean we have found. 097 E bean = getBySystemName(s.getSystemName()); 098 if (bean == null) { 099 // This should never happen. 100 throw new IllegalArgumentException(s.getSystemName() + " is not registered in manager"); 101 } 102 super.deregister(bean); 103 } 104 105 /** 106 * Test if parameter is a properly formatted system name. 107 * 108 * @param systemName the system name 109 * @return enum indicating current validity, which might be just as a prefix 110 */ 111 @Override 112 public final NameValidity validSystemNameFormat(String systemName) { 113 return LogixNG_Manager.validSystemNameFormat( 114 getSubSystemNamePrefix(), systemName); 115 } 116 117 @Override 118 public void register(@Nonnull E s) { 119 throw new RuntimeException("Use BaseManager.registerBean() instead"); 120 } 121 122 @Override 123 public E registerBean(@Nonnull E s) { 124 E bean = s; 125 for (MaleSocketFactory<E> factory : _maleSocketFactories) { 126 bean = factory.encapsulateMaleSocket(this, bean); 127 } 128 super.register(bean); 129 return bean; 130 } 131 132 @Override 133 public void registerMaleSocketFactory(MaleSocketFactory<E> factory) { 134 _maleSocketFactories.add(factory); 135 } 136 137 /** {@inheritDoc} */ 138 @Override 139 @SuppressWarnings("unchecked") // Can't check generic types 140 protected E getOuterBean(E bean) { 141 if (bean == null) { 142 return null; 143 } 144 if (bean instanceof Base) { 145 Base b = (Base) bean; 146 while (b.getParent() instanceof MaleSocket) { 147 b = b.getParent(); 148 } 149 return (E) b; 150 } 151 return bean; 152 } 153 154// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractBaseManager.class); 155 156}