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}