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