001package jmri.jmrit.logixng;
002
003import java.util.Map;
004import java.util.List;
005import javax.annotation.CheckForNull;
006
007/**
008 * A LogixNG female expression socket.
009 * A Expression or a Action that has children must not use
010 * these directly but instead use a FemaleSocket.
011 * 
012 * @author Daniel Bergqvist Copyright 2018
013 */
014public interface FemaleSocket extends Base {
015
016    /**
017     * Connect the male socket to this female socket.
018     * @param socket the socket to connect
019     * @throws SocketAlreadyConnectedException if the socket is already connected
020     */
021    void connect(MaleSocket socket) throws SocketAlreadyConnectedException;
022
023    /**
024     * Disconnect the current connected male socket from this female socket.
025     */
026    void disconnect();
027    
028    /**
029     * Can a connected socket be disconnected?
030     * @return true if the socket can be disconnected, false otherwise
031     */
032    default boolean canDisconnect() {
033        return true;
034    }
035    
036    /**
037     * Get the connected socket.
038     * @return the male socket or null if not connected
039     */
040    MaleSocket getConnectedSocket();
041    
042    /**
043     * Is a male socket connected to this female socket?
044     * @return true if connected
045     */
046    boolean isConnected();
047    
048    /**
049     * Is a particular male socket compatible with this female socket?
050     * @param socket the male socket
051     * @return true if the male socket can be connected to this female socket
052     */
053    boolean isCompatible(MaleSocket socket);
054    
055    /**
056     * Validates a name for a FemaleSocket.
057     * <P>
058     * The name must have at least one character and only alphanumeric
059     * characters. The first character must not be a digit.
060     * 
061     * @param name the name
062     * @return true if the name is valid, false otherwise
063     */
064    default boolean validateName(String name) {
065        return validateName(name, false);
066    }
067    
068    /**
069     * Validates a name for a FemaleSocket.
070     * <P>
071     * The name must have at least one character and only alphanumeric
072     * characters. The first character must not be a digit.
073     * 
074     * @param name the name
075     * @param ignoreDuplicateErrors true if duplicate names should be ignored,
076     *                              false otherwise
077     * @return true if the name is valid, false otherwise
078     */
079    boolean validateName(String name, boolean ignoreDuplicateErrors);
080    
081    /**
082     * Set the name of this socket.
083     * <P>
084     * The name must have at least one character and only alphanumeric
085     * characters. The first character must not be a digit.
086     * 
087     * @param name the name
088     */
089    default void setName(String name) {
090        setName(name, false);
091    }
092    
093    /**
094     * Set the name of this socket.
095     * <P>
096     * The name must have at least one character and only alphanumeric
097     * characters. The first character must not be a digit.
098     * 
099     * @param name the name
100     * @param ignoreDuplicateErrors true if duplicate names should be ignored,
101     *                              false otherwise
102     */
103    void setName(String name, boolean ignoreDuplicateErrors);
104    
105    /**
106     * Get the name of this socket.
107     * @return the name
108     */
109    @CheckForNull
110    String getName();
111    
112    /**
113     * Is the operation allowed on this socket?
114     * @param oper the operation to do
115     * @return true if operation is allowed, false otherwise
116     */
117    default boolean isSocketOperationAllowed(FemaleSocketOperation oper) {
118        Base parent = getParent();
119        if (parent == null) return false;
120        
121        for (int i=0; i < parent.getChildCount(); i++) {
122            if (parent.getChild(i) == this) {
123                return parent.isSocketOperationAllowed(i, oper);
124            }
125        }
126        throw new IllegalArgumentException("Invalid index");
127    }
128
129    /**
130     * Do an operation on this socket
131     * @param oper the operation to do
132     */
133    default void doSocketOperation(FemaleSocketOperation oper) {
134        Base parent = getParent();
135        for (int i=0; i < parent.getChildCount(); i++) {
136            if (parent.getChild(i) == this) {
137                parent.doSocketOperation(i, oper);
138                return;
139            }
140        }
141        throw new IllegalArgumentException("Invalid index");
142    }
143
144    /**
145     * Sets whenever listeners are enabled or not.
146     * ConditionalNG has always listeners enabled, but Clipboard and Module
147     * has never listeners enabled.
148     * @param enable true if listeners should be enabled, false otherwise
149     */
150    void setEnableListeners(boolean enable);
151    
152    /**
153     * Gets whenever listeners are enabled or not.
154     * ConditionalNG has always listeners enabled, but Clipboard and Module
155     * has never listeners enabled.
156     * @return true if listeners should be enabled, false otherwise
157     */
158    boolean getEnableListeners();
159    
160    /**
161     * Am I an ancestor to this maleSocket?
162     * 
163     * @param maleSocket the maleSocket that could be a child
164     * @return true if this oject is an ancestor to the maleSocket object
165     */
166    default boolean isAncestor(MaleSocket maleSocket) {
167        Base base = maleSocket;
168        while ((base != null) && (base != this)) {
169            base = base.getParent();
170        }
171        return base == this;
172    }
173    
174    /**
175     * Get a set of classes that are compatible with this female socket.
176     * 
177     * @return a set of entries with category and class
178     */
179    Map<Category, List<Class<? extends Base>>> getConnectableClasses();
180    
181    /** {@inheritDoc} */
182    @Override
183    default void setup() {
184        if (isConnected()) {
185            getConnectedSocket().setup();
186        }
187    }
188    
189}