001package jmri.jmrit.logixng.swing;
002
003import java.util.*;
004
005import javax.annotation.CheckForNull;
006import javax.annotation.Nonnull;
007import javax.swing.JComponent;
008import javax.swing.JDialog;
009import javax.swing.JLabel;
010import javax.swing.JPanel;
011
012import jmri.NamedBean;
013import jmri.NamedBean.BadUserNameException;
014import jmri.NamedBean.BadSystemNameException;
015import jmri.jmrit.logixng.Base;
016import jmri.jmrit.logixng.BaseManager;
017import jmri.jmrit.logixng.MaleSocket;
018
019/**
020 * The parent interface for configuring classes with Swing.
021 *
022 * @author Daniel Bergqvist Copyright 2018
023 */
024public interface SwingConfiguratorInterface extends Comparable<SwingConfiguratorInterface> {
025
026    /**
027     * Set the dialog of this SWI.
028     *
029     * @param dialog the dialog
030     */
031    public void setJDialog(JDialog dialog);
032
033    /**
034     * Set the dialog of this SWI.
035     *
036     * @return the dialog
037     */
038    public JDialog getJDialog();
039
040    /**
041     * Get the menu text for execute/evaluate.
042     * @return the menu text
043     */
044    public String getExecuteEvaluateMenuText();
045
046    /**
047     * Execute or evaluate an item that this object configures.
048     * @param object the object to execute or evaluate
049     */
050    public void executeEvaluate(@Nonnull Base object);
051
052    /**
053     * Get the manager that handles the beans for the new object.
054     * This is used for validation of the system name for the bean that this
055     * class creates.
056     *
057     * @return the manager
058     */
059    public BaseManager<? extends NamedBean> getManager();
060
061    /**
062     * Get a configuration panel when a new object is to be created and we don't
063     * have it yet.
064     * This method initializes the panel with an empty configuration.
065     *
066     * @param buttonPanel panel with the buttons
067     * @return a panel that configures this object
068     * @throws IllegalArgumentException if this class does not support the class
069     * with the name given in parameter 'className'
070     */
071    public JPanel getConfigPanel(JPanel buttonPanel) throws IllegalArgumentException;
072
073    /**
074     * Get a configuration panel for an object.
075     * This method initializes the panel with the configuration of the object.
076     *
077     * @param object the object for which to return a configuration panel
078     * @param buttonPanel panel with the buttons
079     * @return a panel that configures this object
080     */
081    public JPanel getConfigPanel(@Nonnull Base object, JPanel buttonPanel) throws IllegalArgumentException;
082
083    /**
084     * Validate the form.
085     * <P>
086     * The parameter errorMessage is used to give the error message in case of
087     * an error. If there are errors, the error messages is added to the list
088     * errorMessage.
089     *
090     * @param errorMessages the error messages in case of an error
091     * @return true if data in the form is valid, false otherwise
092     */
093    public boolean validate(@Nonnull List<String> errorMessages);
094
095    /**
096     * Get an example of a system name
097     * @return the system name
098     */
099    public String getExampleSystemName();
100
101    /**
102     * Create a new system name.
103     * @return a new system name
104     */
105    public String getAutoSystemName();
106
107    /**
108     * Create a new object with the data entered.This method must also register the object in its manager.
109     *
110     * @param systemName system name
111     * @param userName user name
112     * @return a male socket for the new object
113     */
114    @Nonnull
115    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName)
116            throws BadUserNameException, BadSystemNameException;
117
118    /**
119     * Updates the object with the data in the form.
120     *
121     * @param object the object to update
122     */
123    public void updateObject(@Nonnull Base object);
124
125    /**
126     * Returns the name of the class that this class configures.
127     *
128     * @return the name of the class this class configures.
129     */
130    @Override
131    public String toString();
132
133    /**
134     * Set default values for this dialog.
135     * This method is used by tests to be able to run validation without
136     * knowing which data that needs to be entered for the validation to pass.
137     */
138    public default void setDefaultValues() {
139        // Do nothing
140    }
141
142    /**
143     * Is the SWI ready to be closed?
144     * @return true if the SWI is ready to be closed, false otherwise.
145     */
146    public default boolean canClose() {
147        return true;
148    }
149
150    /**
151     * Dispose the panel and remove all the listeners that this class may have
152     * registered.
153     */
154    public void dispose();
155
156
157    /**
158     * Parses the message and creates a list of components there the given
159     * components are separated by JLabel components from the message.
160     * @param message the message to be parsed
161     * @param components the components
162     * @return the components separated with JLabel components
163     */
164    public static List<JComponent> parseMessage(String message, JComponent[] components) {
165        List<JComponent> componentsToReturn = new ArrayList<>();
166        StringBuilder sb = new StringBuilder();
167        boolean parseNumber = false;
168
169        for (int index=0; index < message.length(); index++) {
170            int character = message.codePointAt(index);
171
172            if (parseNumber) {
173                if (character == '}') {
174                    int number = Integer.parseInt(sb.toString());
175                    componentsToReturn.add(components[number]);
176                    sb = new StringBuilder();
177                    parseNumber = false;
178                } else if ((character >= '0') && (character <= '9')) {
179                    sb.appendCodePoint(character);
180                } else {
181                    throw new IllegalArgumentException(
182                            "left curly bracket must be followed by a digit but is followed by "
183                                    + Arrays.toString(Character.toChars(character)));
184                }
185            } else {
186                if (character == '{') {
187                    parseNumber = true;
188                    componentsToReturn.add(new JLabel(sb.toString()));
189                    sb = new StringBuilder();
190                } else {
191                    sb.appendCodePoint(character);
192                }
193            }
194        }
195
196        if (sb.length() > 0) componentsToReturn.add(new JLabel(sb.toString()));
197
198        return componentsToReturn;
199    }
200
201    /** {@inheritDoc} */
202    @Override
203    public default int compareTo(SwingConfiguratorInterface swi) {
204        return toString().compareTo(swi.toString());
205    }
206
207}