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//    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractBaseManager.class);
138    
139}