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