public class CuesController {

    private final RemoteModel remoteModel;
    private final CuesView cuesView;

    /**
     * The constructor of the LogsController class.
     *
     * @param remoteModel
     *            the model associated with this Controller
     * @param cuesView
     *            the view associated with this Controller
     */
    public CuesController(final RemoteModel remoteModel, final CuesView cuesView) {
        this.remoteModel = remoteModel;
        this.cuesView = cuesView;

        this.initButtonsListeners();
    }

    private void initButtonsListeners() {
        this.cuesView.addStoreButtonListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent arg0) {
                final String cueName = CuesViewHelper.askForCueName(cuesView
                        .getViewComponent(), remoteModel.getCuesListModel()
                        .getNextDefaultCueName());

                if (cueName != null) {
                    try {
                        remoteModel.storeCue(cueName);
                    } catch (Exception exception) {
                        CuesViewHelper.printError(cuesView.getViewComponent(),
                                exception, cueName);
                    }
                }
            }
        });

        this.cuesView.addLoadButtonListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                Cue cue = cuesView.getSelectedCue();
                if (cue != null) {
                    remoteModel.loadCue(cue);
                }
            }
        });

        this.cuesView.addRemoveButtonListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent arg0) {
                Cue cue = cuesView.getSelectedCue();
                if (cue != null
                        && CuesViewHelper.confirmCueRemove(
                                cuesView.getViewComponent(), cue.getName())) {
                    remoteModel.removeCue(cue);
                }
            }
        });
    }
} public class DMXController {

    private final DMXTableModel dmxTableModel;
    private final DMXView dmxView;

    /**
     * The constructor of the DMXController class.
     *
     * @param dmxTableModel
     *            the dmx table model associated with this Controller
     * @param dmxView
     *            the view associated with this Controller
     */
    public DMXController(final DMXTableModel dmxTableModel,
            final DMXView dmxView) {
        this.dmxTableModel = dmxTableModel;
        this.dmxView = dmxView;

        this.initListeners();
    }

    private void initListeners() {
        this.dmxView.addInOutRadioActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent actionEvent) {
                dmxTableModel.setInputEnabled(actionEvent.getActionCommand()
                        .equals("input"));
            }
        });
    }
} public class LogsController {

    private final RemoteModel remoteModel;
    private final LogsView logsView;

    /**
     * The constructor of the LogsController class.
     *
     * @param remoteModel
     *            the model associated with this Controller
     * @param logsView
     *            the view associated with this Controller
     */
    public LogsController(final RemoteModel remoteModel, final LogsView logsView) {
        this.remoteModel = remoteModel;
        this.logsView = logsView;

        this.initButtonsListeners();
    }

    private void initButtonsListeners() {
        this.logsView.addClearLogsButtonListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                remoteModel.getLogsListModel().clearLogsList();
            }
        });
    }
} public class PrefsController {

    private final RemoteModel remoteModel;
    private final PrefsView prefsView;

    /**
     * The default constructor for the prefs controller.
     *
     * @param remoteModel
     *            the model associated to the controller
     * @param localeModel
     * @param prefsView
     *            the view associated to the controller
     */
    public PrefsController(final RemoteModel remoteModel,
            final LocaleComboBoxModel localeModel, final PrefsView prefsView) {
        this.remoteModel = remoteModel;
        this.prefsView = prefsView;
        this.initListeners();
    }

    private void initListeners() {
        this.prefsView.addCancelButtonListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                remoteModel.restoreArtNetServerManagerConfig();
            }
        });

        this.prefsView.addSaveButtonListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                try {
                    remoteModel.applyArtNetServerManagerConfig();
                } catch (ArtNetException ane) {
                    JOptionPane.showMessageDialog(
                            null,
                            MessageFormat.format(
                                    Messages.getString("error.server.cannotstart.message"),
                                    ane.getMessage()),
                            Messages.getString("error.server.cannotstart.title"),
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        });

        this.prefsView
                .addEnableHttpServerCheckBoxListener(new ActionListener() {
                    @Override
                    public void actionPerformed(final ActionEvent e) {
                        remoteModel.setHttpServerEnabled(((JCheckBox) e
                                .getSource()).isSelected());
                    }
                });

        this.prefsView.addLangComboListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                JComboBox cb = (JComboBox) e.getSource();
                Locale l = (Locale) cb.getSelectedItem();
                JOptionPane.showMessageDialog(null, MessageFormat.format(
                        Messages.getString("prefsview.lang.restart"),
                        Messages.getString("ui.lang." + l.getLanguage())));
            }
        });
    }
} public class RemoteController {

    private final RemoteModel remoteModel;
    private final RemoteView remoteView;

    private final transient Logger logger = LoggersManager.getInstance()
            .getLogger(RemoteController.class.getName());

    /**
     * The default constructor for the remote controller.
     *
     * @param remoteModel
     *            model that contains the state of the remote.
     * @param remoteView
     *            view that display the remote.
     */
    public RemoteController(final RemoteModel remoteModel,
            final RemoteView remoteView) {
        this.remoteModel = remoteModel;
        this.remoteView = remoteView;

        this.initButtonsListeners();
        this.initKeyStrokes();
    }

    private void initKeyStrokes() {
        InputMap inputMap = this.remoteView.getRemotePanelInputMap();
        ActionMap actionMap = this.remoteView.getRemotePanelActionMap();
        for (Character c : this.remoteView.getValuesList()) {
            this.initValueKeyStroke(inputMap, actionMap, c);
        }

        inputMap.put(KeyStroke.getKeyStroke("BACK_SPACE"), "backspace");
        actionMap.put("backspace", new AbstractAction() {

            private static final long serialVersionUID = -4335381990869042671L;

            @Override
            public void actionPerformed(final ActionEvent e) {
                if (remoteView.isDelButtonEnabled()) {
                    remoteModel.delLastCommandLineChar();
                }
            }
        });

        inputMap.put(KeyStroke.getKeyStroke("ENTER"), "enter");
        actionMap.put("enter", new AbstractAction() {

            private static final long serialVersionUID = -1152279417262923317L;

            @Override
            public void actionPerformed(final ActionEvent e) {
                if (remoteView.isEnterButtonEnabled()) {
                    try {
                        remoteModel.sendCommand();
                    } catch (BadSyntaxException exception) {
                        logger.severe("Bad syntax in Command Line");
                    }
                }
            }
        });

        inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
        actionMap.put("escape", new AbstractAction() {

            private static final long serialVersionUID = -8772359436304401320L;

            @Override
            public void actionPerformed(final ActionEvent e) {
                if (remoteView.isResetButtonEnabled()) {
                    remoteModel.resetCommandLine();
                }
            }
        });
    }

    private void initValueKeyStroke(final InputMap inputMap,
            final ActionMap actionMap, final Character c) {
        inputMap.put(KeyStroke.getKeyStroke(c), c);
        actionMap.put(c, new AbstractAction() {

            private static final long serialVersionUID = 6382574213528201626L;

            @Override
            public void actionPerformed(final ActionEvent e) {
                remoteModel.addToCommandLine(c);
            }
        });
    }

    private void initButtonsListeners() {
        this.remoteView.addValueButtonsListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                String buttonText = ((JButton) e.getSource()).getText();
                remoteModel.addToCommandLine(buttonText.charAt(0));
            }
        });
        this.remoteView.addDelButtonListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                remoteModel.delLastCommandLineChar();
            }
        });
        this.remoteView.addEnterButtonListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                try {
                    remoteModel.sendCommand();
                } catch (BadSyntaxException exception) {
                    logger.severe("Bad syntax in Command Line");
                }
            }
        });
        this.remoteView.addResetButtonListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                remoteModel.resetCommandLine();
            }
        });
    }
} public class DMXTableModel extends DefaultTableModel {

    private static final long serialVersionUID = -3171182606809834583L;
    private static final int COLUMN_COUNT = 16;
    private static final int ROW_COUNT = 512 / COLUMN_COUNT;
    private static final Integer[] ROW_HEADERS;

    private final ArtNetServerManager artNetServerManager;

    private final SwingWorker<Void, byte[]> dmxArrayUpdater;

    private boolean inputEnabled = false;

    static {
        ROW_HEADERS = new Integer[ROW_COUNT];
        for (int i = 0; i < ROW_COUNT; i++) {
            ROW_HEADERS[i] = i * COLUMN_COUNT;
        }
    }

    /**
     * Default constructor of the class.
     *
     * @param serverManager
     *            the server manager used by the model
     */
    public DMXTableModel(final ArtNetServerManager serverManager) {
        this.artNetServerManager = serverManager;
        dmxArrayUpdater = new SwingWorker<Void, byte[]>() {

            @Override
            protected Void doInBackground() throws Exception {
                while (true) {
                    fireTableDataChanged();
                    Thread.sleep(40);
                }
            }

        };

        dmxArrayUpdater.execute();
    }

    @Override
    public int getColumnCount() {
        return COLUMN_COUNT;
    }

    @Override
    public String getColumnName(final int column) {
        return Integer.toString(column + 1);
    }

    @Override
    public int getRowCount() {
        return ROW_COUNT;
    }

    @Override
    public Integer getValueAt(final int row, final int column) {
        int index = column + row * COLUMN_COUNT;
        int intValue = inputEnabled ? ((Byte) artNetServerManager
                .getCurrentInputDmxArray()[index]).intValue()
                : ((Byte) artNetServerManager.getCurrentOutputDmxArray()[index])
                        .intValue();
        return intValue < 0 ? intValue + 256 : intValue;
    }

    @Override
    public Class<?> getColumnClass(final int columnIndex) {
        return Integer.class;
    }

    /**
     * get the table row headers.
     *
     * @return an integer array representing the row headers
     */
    public Integer[] getRowHeaders() {
        return ROW_HEADERS;
    }

    /**
     * dispose the model before closing the application.
     */
    public void dispose() {
        dmxArrayUpdater.cancel(true);
    }

    /**
     * Gets the status of input enabling.
     *
     * @return true if input is enabled, false if output is enabled
     */
    public boolean isInputEnabled() {
        return inputEnabled;
    }

    /**
     * Set the status of input enabling.
     *
     * @param inputEnabled
     *            the status of input enabling
     */
    public void setInputEnabled(final boolean inputEnabled) {
        this.inputEnabled = inputEnabled;
    }

} public class LocaleComboBoxModel extends DefaultComboBoxModel {

    /**
     * uid of serialization.
     */
    private static final long serialVersionUID = -1488500296982883031L;

    /**
     * Locales available for i18n.
     */
    private Locale[] locales = new Locale[] { Locale.ENGLISH, Locale.FRENCH, };

    /**
     * {@inheritDoc}
     */
    @Override
    public Locale getElementAt(final int index) {
        return locales[index];
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getSize() {
        return locales.length;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setSelectedItem(final Object anObject) {
        super.setSelectedItem(anObject);
    }
} public class RemoteModel {
    private LogsListModel logsListModel;
    private SpinnerNumberModel inPortSpinnerModel;
    private SpinnerNumberModel outPortSpinnerModel;
    private SpinnerNumberModel universeSpinnerModel;
    private SpinnerNumberModel subnetSpinnerModel;
    private SpinnerNumberModel httpPortSpinnerModel;
    private ComboBoxModel broadcastAddressComboModel;

    private StringBuilder commandLine;
    private boolean httpServerEnabled;
    private boolean additiveModeEnabled;

    private List<RemoteModelListener> remoteModelChangedListeners;

    private final ArtNetServerManager artNetServerManager;
    private final HttpServerManager httpServerManager;
    private final CuesManager cuesManager;
    private CuesListModel cuesListModel;

    private static final int MIN_PORT = 0;
    private static final int MAX_PORT = 65535;
    private static final int MIN_SUBNET = 0;
    private static final int MAX_SUBNET = 15;
    private static final int MIN_UNIVERSE = 0;
    private static final int MAX_UNIVERSE = 15;

    private final Logger logger = LoggersManager.getInstance().getLogger(
            RemoteModel.class.getCanonicalName());

    /**
     * Broadcast Address.
     *
     * @author Alexandre COLLIGNON
     */
    public enum BroadCastAddress {

        /** Primary broadcast address. **/
        PRIMARY("2.255.255.255"),

        /** Secondary broadcast address. **/
        SECONDAY("10.255.255.255"),

        /** Local broadcast address. **/
        LOCAL("127.255.255.255");

        /** Address representation. **/
        private String address;

        /** Private constructor. **/
        private BroadCastAddress(final String address) {
            this.address = address;
        }

        /**
         * Returns the String representation of the address.
         *
         * @return String representation of the address
         */
        public String getAddress() {
            return address;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return address;
        }
    }

    /**
     * Default constructor of the remote model.
     *
     * @param serverManager
     *            the server manager used by the model
     * @param httpManager
     *            the http manager used by the model
     * @param cuesManager
     *            the cues manager used by the model
     */
    public RemoteModel(final ArtNetServerManager serverManager,
            final HttpServerManager httpManager, final CuesManager cuesManager) {
        this.artNetServerManager = serverManager;
        this.httpServerManager = httpManager;
        this.cuesManager = cuesManager;
        this.logsListModel = new LogsListModel();
        this.cuesListModel = new CuesListModel(this.cuesManager);
        this.logsListModel.addLogger(ArtNet.logger);

        this.inPortSpinnerModel = new SpinnerNumberModel(
                ArtNetServerManager.DEFAULT_ARTNET_PORT, RemoteModel.MIN_PORT,
                RemoteModel.MAX_PORT, 1);
        this.outPortSpinnerModel = new SpinnerNumberModel(
                ArtNetServerManager.DEFAULT_ARTNET_PORT, RemoteModel.MIN_PORT,
                RemoteModel.MAX_PORT, 1);
        this.subnetSpinnerModel = new SpinnerNumberModel(0,
                RemoteModel.MIN_SUBNET, RemoteModel.MAX_SUBNET, 1);
        this.universeSpinnerModel = new SpinnerNumberModel(0,
                RemoteModel.MIN_UNIVERSE, RemoteModel.MAX_UNIVERSE, 1);
        this.httpPortSpinnerModel = new SpinnerNumberModel(
                HttpServerManager.DEFAULT_HTTP_PORT, RemoteModel.MIN_PORT,
                RemoteModel.MAX_PORT, 1);
        this.broadcastAddressComboModel = new DefaultComboBoxModel(
                BroadCastAddress.values());

        this.commandLine = new StringBuilder();
        this.remoteModelChangedListeners = new ArrayList<RemoteModelListener>();
        this.initModelsListeners();
    }

    private void initModelsListeners() {
        this.broadcastAddressComboModel
                .addListDataListener(new ListDataListener() {
                    @Override
                    public void intervalRemoved(final ListDataEvent e) {
                    }

                    @Override
                    public void intervalAdded(final ListDataEvent e) {
                    }

                    @Override
                    public void contentsChanged(final ListDataEvent e) {
                        artNetServerManager
                                .setBroadcastAddress(((BroadCastAddress) broadcastAddressComboModel
                                        .getSelectedItem()).getAddress());
                    }
                });

        this.subnetSpinnerModel.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(final ChangeEvent e) {
                artNetServerManager.setSubnet((Integer) subnetSpinnerModel
                        .getValue());
            }
        });

        this.universeSpinnerModel.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(final ChangeEvent e) {
                artNetServerManager.setUniverse((Integer) universeSpinnerModel
                        .getValue());
            }
        });

        this.httpPortSpinnerModel.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(final ChangeEvent e) {
                httpServerManager.setInPort((Integer) httpPortSpinnerModel
                        .getValue());
            }
        });

        this.artNetServerManager
                .addArtNetServerManagerChangedListener(new ArtNetServerManagerListener() {

                    @Override
                    public void universeValueChanged(
                            final UniverseValueChangedEvent event) {
                        universeSpinnerModel.setValue(event.getUniverse());
                    }

                    @Override
                    public void subnetValueChanged(
                            final SubnetValueChangedEvent event) {
                        subnetSpinnerModel.setValue(event.getSubnet());
                    }

                    @Override
                    public void additiveModeValueChanged(
                            final AdditiveModeValueChangedEvent event) {
                        additiveModeEnabled = event.isAdditiveModeEnabled();
                        fireAdditiveModeValueChanged();
                    }
                });
    }

    /**
     * Add a character to the command line.
     *
     * @param c
     *            the character added
     */
    public void addToCommandLine(final Character c) {
        this.commandLine.append(c);
        this.fireCommandLineValueChanged();
    }

    /**
     * Resets value of the command line.
     */
    public void resetCommandLine() {
        this.commandLine.delete(0, this.commandLine.length());
        this.fireCommandLineValueChanged();
    }

    /**
     * Deletes the last character of the command line.
     */
    public void delLastCommandLineChar() {
        this.commandLine.delete(this.commandLine.length() - 1,
                this.commandLine.length());
        this.fireCommandLineValueChanged();
    }

    /**
     * Returns the value of the command line.
     *
     * @return the value of the command line
     */
    public String getCommandLineValue() {
        return this.commandLine.toString();
    }

    /**
     * process the command line and send a Dmx command over the network.
     *
     * @throws BadSyntaxException
     *             if the command line to process and send has a bad syntax
     */
    public void sendCommand() throws BadSyntaxException {
        artNetServerManager.processCommandLine(this.commandLine.toString());
        artNetServerManager.sendDmxCommand();
        this.resetCommandLine();
    }

    /**
     * Starts the ArtNet Server.
     *
     * @throws ArtNetException
     *             if a server is already running
     * @throws SocketException
     *             if there is a problem with the server socket
     */
    public void startArtNet() throws SocketException, ArtNetException {
        artNetServerManager.startArtNet();
        this.fireArtNetStarted();
    }

    /**
     * Stops the ArtNet Server.
     */
    public void stopArtNet() {
        artNetServerManager.stopArtNet();
        this.fireArtNetStopped();
    }

    /**
     * Starts the Http Server.
     *
     * @throws IOException
     *             if the server is unable to start
     */
    public void startHttp() throws IOException {
        httpServerManager.startHttp();
        this.fireHttpStarted();
    }

    /**
     * Stops the Http Server.
     */
    public void stopHttp() {
        httpServerManager.stopHttp();
        this.fireHttpStopped();
    }

    private void fireArtNetStarted() {
        for (RemoteModelListener listener : this.remoteModelChangedListeners) {
            ArtNetStartedEvent e = new ArtNetStartedEvent();
            listener.artNetStarted(e);
        }
    }

    private void fireArtNetStopped() {
        for (RemoteModelListener listener : this.remoteModelChangedListeners) {
            ArtNetStoppedEvent e = new ArtNetStoppedEvent();
            listener.artNetStopped(e);
        }
    }

    private void fireHttpStarted() {
        for (RemoteModelListener listener : this.remoteModelChangedListeners) {
            HttpStartedEvent e = new HttpStartedEvent();
            listener.httpStarted(e);
        }
    }

    private void fireHttpStopped() {
        for (RemoteModelListener listener : this.remoteModelChangedListeners) {
            HttpStoppedEvent e = new HttpStoppedEvent();
            listener.httpStopped(e);
        }
    }

    private void fireCommandLineValueChanged() {
        for (RemoteModelListener listener : this.remoteModelChangedListeners) {
            CommandLineValueChangedEvent e = new CommandLineValueChangedEvent(
                    this.getCommandLineValue());
            listener.commandLineValueChanged(e);
        }
    }

    private void fireAdditiveModeValueChanged() {
        for (RemoteModelListener listener : this.remoteModelChangedListeners) {
            AdditiveModeValueChangedEvent e = new AdditiveModeValueChangedEvent(
                    this.isAdditiveModeEnabled());
            listener.additiveModeValueChanged(e);
        }
    }

    /**
     * Returns the model of the logs list.
     *
     * @return the Logs list model
     */
    public LogsListModel getLogsListModel() {
        return this.logsListModel;
    }

    /**
     * Gets the model of the in port.
     *
     * @return the in port spinner model
     */
    public SpinnerModel getInPortSpinnerModel() {
        return this.inPortSpinnerModel;
    }

    /**
     * Gets the model of the out port.
     *
     * @return the out port spinner model
     */
    public SpinnerModel getOutPortSpinnerModel() {
        return this.outPortSpinnerModel;
    }

    /**
     * Gets the model of the subnet.
     *
     * @return the subnet spinner model
     */
    public SpinnerModel getSubnetSpinnerModel() {
        return this.subnetSpinnerModel;
    }

    /**
     * Gets the model of the universe.
     *
     * @return the universe spinner model
     */
    public SpinnerModel getUniverseSpinnerModel() {
        return this.universeSpinnerModel;
    }

    /**
     * Gets the model of the http port.
     *
     * @return the http port spinner model
     */
    public SpinnerModel getHttpPortSpinnerModel() {
        return this.httpPortSpinnerModel;
    }

    /**
     * Gets the model of the broadcast address.
     *
     * @return the broadcast address combo model
     */
    public ComboBoxModel getBroadcastAddressComboModel() {
        return this.broadcastAddressComboModel;
    }

    /**
     * Give the status of the http server enabling.
     *
     * @return true if http server is enabled, false if disabled
     */
    public boolean isHttpServerEnabled() {
        return this.httpServerEnabled;
    }

    /**
     * Enable or disable the http server.
     *
     * @param httpServerEnabled
     *            true to enable the http server, false to disable
     */
    public void setHttpServerEnabled(final boolean httpServerEnabled) {
        this.httpServerEnabled = httpServerEnabled;
    }

    /**
     * Give the status of the additive mode enabling.
     *
     * @return true if additive mode is enabled, false if disabled
     */
    public boolean isAdditiveModeEnabled() {
        return this.additiveModeEnabled;
    }

    /**
     * Enable or disable the additive mode.
     *
     * @param additiveModeEnabled
     *            true to enable the additive mode, false to disable
     */
    public void setAdditiveModeEnabled(final boolean additiveModeEnabled) {
        this.additiveModeEnabled = additiveModeEnabled;
        artNetServerManager.setAdditiveModeEnabled(this.additiveModeEnabled);
    }

    /**
     * Adds an element to the list of listener of the remote model.
     *
     * @param listener
     *            the listener to add
     */
    public void addRemoteModelChangedListener(final RemoteModelListener listener) {
        this.remoteModelChangedListeners.add(listener);
    }

    /**
     * Removes an element to the list of listener of the remote model.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeRemoteModelChangedListener(
            final RemoteModelListener listener) {
        this.remoteModelChangedListeners.remove(listener);
    }

    /**
     * Returns the input port.
     *
     * @return the input port
     */
    public int getInPort() {
        return getSelectedIntValue(inPortSpinnerModel);
    }

    /**
     * Returns the output port.
     *
     * @return the output port
     */
    public int getOutPort() {
        return getSelectedIntValue(outPortSpinnerModel);
    }

    /**
     * Returns the universe.
     *
     * @return the universe
     */
    public int getUniverse() {
        return getSelectedIntValue(universeSpinnerModel);
    }

    /**
     * Returns the subnet.
     *
     * @return the subnet
     */
    public int getSubnet() {
        return getSelectedIntValue(subnetSpinnerModel);
    }

    /**
     * Returns the Http port.
     *
     * @return the Http port
     */
    public int getHttpPort() {
        return getSelectedIntValue(httpPortSpinnerModel);
    }

    /**
     * Returns the broadcast address.
     *
     * @return the broadcast address
     */
    public BroadCastAddress getBroadCastAddress() {
        return (BroadCastAddress) broadcastAddressComboModel.getSelectedItem();
    }

    /**
     * Returns the selected value of the model casted to int (no check).
     *
     * @param model
     *            the model
     * @return the selected value of the model
     */
    private int getSelectedIntValue(final SpinnerModel model) {
        return (Integer) model.getValue();
    }

    /**
     * Sets the input port.
     *
     * @param inPort
     *            the input port
     */
    public void setInPort(final int inPort) {
        inPortSpinnerModel.setValue(inPort);
    }

    /**
     * Sets the output port.
     *
     * @param outPort
     *            the output port
     */
    public void setOutputPort(final int outPort) {
        outPortSpinnerModel.setValue(outPort);
    }

    /**
     * Sets the universe.
     *
     * @param universe
     *            the universe
     */
    public void setUniverse(final int universe) {
        universeSpinnerModel.setValue(universe);
    }

    /**
     * Sets the subnet.
     *
     * @param subnet
     *            the subnet
     */
    public void setSubnet(final int subnet) {
        subnetSpinnerModel.setValue(subnet);
    }

    /**
     * Sets the Http port.
     *
     * @param httpPort
     *            the Http port
     */
    public void setHttpPort(final int httpPort) {
        httpPortSpinnerModel.setValue(httpPort);
    }

    /**
     * set the value of the broadcast address.
     *
     * @param broadcastAddress
     *            the broadcast address
     */
    public void setBroadCastAddress(final BroadCastAddress broadcastAddress) {
        broadcastAddressComboModel.setSelectedItem(broadcastAddress);
    }

    /**
     * Restores the configuration according to the current configuration of the
     * {@link ArtNetServerManager}.
     */
    public void restoreArtNetServerManagerConfig() {
        inPortSpinnerModel.setValue(artNetServerManager.getInPort());
        outPortSpinnerModel.setValue(artNetServerManager.getOutPort());
    }

    /**
     * Applies the configuration to the {@link ArtNetServerManager}. As a
     * consequence, the server is restarted.
     *
     * @throws ArtNetException
     *             exception thrown if server can't be started.
     */
    public void applyArtNetServerManagerConfig() throws ArtNetException {
        artNetServerManager.setInPort((Integer) inPortSpinnerModel.getValue());
        artNetServerManager
                .setOutPort((Integer) outPortSpinnerModel.getValue());
        this.stopArtNet();
        this.stopHttp();
        try {
            this.startArtNet();
            if (isHttpServerEnabled()) {
                try {
                    this.startHttp();
                } catch (IOException e) {
                    new ArtNetException(e.getMessage(), e.getCause());
                }
            }
        } catch (SocketException e) {
            throw new ArtNetException(e.getMessage(), e.getCause());
        }
    }

    /**
     * Gets the model of the cues list.
     *
     * @return the cues list model
     */
    public CuesListModel getCuesListModel() {
        return this.cuesListModel;
    }

    /**
     * Stores the current state of the dmx array in a cue.
     *
     * @param cueName
     *            the name of the cue to store
     */
    public void storeCue(final String cueName) {
        cuesListModel.addCue(new Cue(cueName, artNetServerManager
                .getCurrentOutputDmxArray()));
    }

    /**
     * Load the given cue.
     *
     * @param cue
     *            the cue to load
     */
    public void loadCue(final Cue cue) {
        artNetServerManager.setCurrentOutputDmxArray(cue.getDmxArray());
        artNetServerManager.sendDmxCommand();
        logger.info("Cue [" + cue.getName() + "] sent");
    }

    /**
     * Removes the given cue from the model.
     *
     * @param cue
     *            the cue to remove
     */
    public void removeCue(final Cue cue) {
        cuesListModel.removeCue(cue);
    }

    /**
     * Persists the current cues to the given {@link OutputStream}.
     *
     * @param stream
     *            the stream used to store the cues
     * @throws IOException
     *             if something goes wrong when persisting
     * @see CuesManager#load(InputStream)
     */
    public void persist(final OutputStream stream) throws IOException {
        cuesManager.persist(stream);
    }

    /**
     * Loads cues from the given {@link InputStream}.
     *
     * @param stream
     *            {@link InputStream} from which the cues must be load
     * @see CuesManager#persist(OutputStream)
     */
    public void load(final InputStream stream) {
        cuesManager.load(stream);
    }
} public enum BroadCastAddress {

    /** Primary broadcast address. **/
    PRIMARY("2.255.255.255"),

    /** Secondary broadcast address. **/
    SECONDAY("10.255.255.255"),

    /** Local broadcast address. **/
    LOCAL("127.255.255.255");

    /** Address representation. **/
    private String address;

    /** Private constructor. **/
    private BroadCastAddress(final String address) {
        this.address = address;
    }

    /**
     * Returns the String representation of the address.
     *
     * @return String representation of the address
     */
    public String getAddress() {
        return address;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return address;
    }
} public class UpdateModel {

    /**
     * Update frequency.
     *
     * @author acollign
     */
    public enum Frequency {
        /**
         * perform on every start up.
         */
        ALWAYS(-1, "always"),
        /**
         * perform once a day.
         */
        DAILY(86400000, "daily"),
        /**
         * perform once a week.
         */
        WEEKLY(604800000, "weekly"),
        /**
         * perform once a month.
         */
        MONTHLY(2628000000L, "monthly"),
        /**
         * never perform.
         */
        NEVER(-1, "never");

        private long value;
        private String key;

        private Frequency(final long value, final String key) {
            this.value = value;
            this.key = key;
        }

        /**
         * Returns the key.
         *
         * @return the key
         */
        public String getKey() {
            return key;
        }

        /**
         * Returns the value.
         *
         * @return the value
         */
        public long getValue() {
            return value;
        }
    }

    /**
     * Key used to save the update frequency value.
     */
    public static final String UPDATE_FREQ_PREFS_KEY = "update.frequency";

    /**
     * Last update check preference key.
     */
    public static final String UPDATE_LATEST_CHECK_PREFS_KEY = "update.latest.check";

    private final Preferences prefs;

    private final ArrayList<UpdateListener> listeners;

    /**
     * Constructs an {@link UpdateModel} on top of the given {@link Preferences}
     * instance.
     *
     * @param prefs
     *            a {@link Preferences} used to fetch and store update frequency
     *            configuration
     */
    public UpdateModel(final Preferences prefs) {
        this.prefs = prefs;
        this.listeners = new ArrayList<UpdateModel.UpdateListener>();

        this.prefs.addPreferenceChangeListener(new PreferenceChangeListener() {
            @Override
            public void preferenceChange(final PreferenceChangeEvent evt) {
                if (UPDATE_FREQ_PREFS_KEY.equals(evt.getKey())
                        && evt.getNewValue() != null) {

                    fireUpdateFrequencySaved(Frequency.valueOf(evt
                            .getNewValue()));
                }
            }
        });
    }

    /**
     * Returns the current frequency.
     *
     * @return the current frequency
     */
    public Frequency getFrequency() {
        return Frequency.valueOf(prefs.get(UPDATE_FREQ_PREFS_KEY,
                Frequency.ALWAYS.name()));
    }

    /**
     * Save the frequency.
     *
     * @param freq
     *            frequency to save
     */
    public void saveUpdateFrequency(final Frequency freq) {
        prefs.put(UPDATE_FREQ_PREFS_KEY, freq.name());
    }

    /**
     * Marks the version as checked.
     */
    public void markAsChecked() {
        prefs.putLong(UPDATE_LATEST_CHECK_PREFS_KEY, new Date().getTime());
    }

    /**
     * Returns true if the update info must be updated according to the
     * frequency, false otherwise.
     *
     * @return true if the update info must be updated according to the
     *         frequency, false otherwise
     */
    public boolean updateIsNecessary() {
        Frequency frequency = getFrequency();
        if (frequency == Frequency.ALWAYS) {
            return true;
        }
        if (frequency == Frequency.NEVER) {
            return false;
        }
        long latest = prefs.getLong(UpdateModel.UPDATE_LATEST_CHECK_PREFS_KEY,
                Frequency.DAILY.getValue());

        return new Date(latest + frequency.value).before(new Date());
    }

    /**
     * Adds an {@link UpdateListener}.
     *
     * @param l
     *            an {@link UpdateListener}
     */
    public void addUpdateListener(final UpdateListener l) {
        listeners.add(l);
    }

    private void fireUpdateFrequencySaved(final Frequency newValue) {
        for (UpdateListener l : listeners) {
            l.updateFrequencySaved(newValue);
        }
    }

    /**
     * A listener of update frequency change.
     *
     * @author acollign
     */
    public interface UpdateListener {
        /**
         * Called when a new {@link Frequency} value has been saved.
         *
         * @param newValue
         *            the new {@link Frequency} value
         */
        void updateFrequencySaved(Frequency newValue);
    }
} public class AboutView implements ViewInterface {

    private final JPanel aboutPanel = new JPanel();

    /**
     * The constructor of the class.
     */
    public AboutView() {
        JLabel icon = new JLabel(new ImageIcon(getClass().getResource(
                "/net/eliosoft/elios/gui/views/elios_256x138.png")));
        JLabel titleLabel = new JLabel(Messages.getString("aboutview.info"),
                SwingConstants.CENTER);
        aboutPanel.setLayout(new GridLayout(2, 1));
        aboutPanel.add(icon);
        aboutPanel.add(titleLabel);
    }

    /**
     * Returns the about panel.
     *
     * @return the view component
     */
    @Override
    public JComponent getViewComponent() {
        return this.aboutPanel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("aboutview.title");
    }

} public class CuesView implements ViewInterface {

    private final JPanel cuesPanel = new JPanel(new BorderLayout());
    private final RemoteModel remoteModel;

    private final JButton storeButton;
    private final JButton loadButton;
    private final JButton removeButton;
    private final JList cuesList;

    /**
     * The constructor of the class.
     *
     * @param remoteModel
     *            the model associated to the view
     */
    public CuesView(final RemoteModel remoteModel) {
        this.remoteModel = remoteModel;
        cuesList = new JList(this.remoteModel.getCuesListModel());

        cuesList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(final MouseEvent e) {
                if (e.getClickCount() == 2) {
                    int index = cuesList.locationToIndex(e.getPoint());
                    remoteModel.loadCue(remoteModel.getCuesListModel()
                            .getElementAt(index));
                }
            }
        });

        JScrollPane scrollPane = new JScrollPane(cuesList);
        scrollPane
                .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        cuesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        cuesList.setCellRenderer(new DefaultListCellRenderer() {

            private static final long serialVersionUID = -3018960552174916077L;

            @Override
            public Component getListCellRendererComponent(final JList list,
                    final Object value, final int index,
                    final boolean isSelected, final boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index,
                        isSelected, cellHasFocus);
                Cue cue = (Cue) value;
                setText(cue.getName());

                return this;
            }
        });
        cuesList.addListSelectionListener(new ListSelectionListener() {

            @Override
            public void valueChanged(final ListSelectionEvent e) {
                if (getSelectedCue() == null) {
                    loadButton.setEnabled(false);
                    removeButton.setEnabled(false);
                } else {
                    loadButton.setEnabled(true);
                    removeButton.setEnabled(true);
                }
            }
        });

        storeButton = new JButton(
                Messages.getString("cuesview.storebutton"),
                new ImageIcon(
                        Elios.class
                                .getResource("/net/eliosoft/elios/gui/views/document-save-as.png")));
        loadButton = new JButton(
                Messages.getString("cuesview.loadbutton"),
                new ImageIcon(
                        Elios.class
                                .getResource("/net/eliosoft/elios/gui/views/document-open.png")));
        loadButton.setEnabled(false);
        removeButton = new JButton(
                Messages.getString("cuesview.removebutton"),
                new ImageIcon(
                        Elios.class
                                .getResource("/net/eliosoft/elios/gui/views/edit-delete.png")));
        removeButton.setEnabled(false);

        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(storeButton);
        buttonsPanel.add(loadButton);
        buttonsPanel.add(removeButton);

        cuesPanel.add(scrollPane, BorderLayout.CENTER);
        cuesPanel.add(buttonsPanel, BorderLayout.SOUTH);
    }

    /**
     * Returns the cues panel.
     *
     * @return the view component
     */
    @Override
    public JComponent getViewComponent() {
        return this.cuesPanel;
    }

    /**
     * Adds an action listener to the store button.
     *
     * @param listener
     *            the listener to add
     */
    public void addStoreButtonListener(final ActionListener listener) {
        this.storeButton.addActionListener(listener);
    }

    /**
     * Removes an action listener to the store button.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeStoreButtonListener(final ActionListener listener) {
        this.storeButton.removeActionListener(listener);
    }

    /**
     * Adds an action listener to the load button.
     *
     * @param listener
     *            the listener to add
     */
    public void addLoadButtonListener(final ActionListener listener) {
        this.loadButton.addActionListener(listener);
    }

    /**
     * Removes an action listener to the load button.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeLoadButtonListener(final ActionListener listener) {
        this.loadButton.removeActionListener(listener);
    }

    /**
     * Adds an action listener to the remove button.
     *
     * @param listener
     *            the listener to add
     */
    public void addRemoveButtonListener(final ActionListener listener) {
        this.removeButton.addActionListener(listener);
    }

    /**
     * Removes an action listener to the remove button.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeRemoveButtonListener(final ActionListener listener) {
        this.removeButton.removeActionListener(listener);
    }

    /**
     * Gets the selected cue.
     *
     * @return the selected cue
     */
    public Cue getSelectedCue() {
        return (Cue) cuesList.getSelectedValue();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("cuesview.title");
    }
} public class DMXView implements ViewInterface {

    private final DMXTableModel dmxTableModel;
    private final JPanel dmxPanel = new JPanel(new BorderLayout());
    private final int cellWidth = dmxPanel.getFontMetrics(dmxPanel.getFont())
            .stringWidth("255") * 2;
    private final int cellHeight = dmxPanel.getFontMetrics(dmxPanel.getFont())
            .getHeight() * 2;
    private final JRadioButton inRadio;
    private final JRadioButton outRadio;

    /**
     * The constructor of the DMX View.
     *
     * @param remoteModel
     *            the RemoteModel used by the view
     * @param tableModel
     *            the DMXTableModel used by the view
     */
    public DMXView(final RemoteModel remoteModel, final DMXTableModel tableModel) {
        this.dmxTableModel = tableModel;

        ButtonGroup inOutRadioGroup = new ButtonGroup();
        JPanel inOutRadioPanel = new JPanel();
        inRadio = new JRadioButton(Messages.getString("dmxview.inputradio"),
                false);
        inRadio.setActionCommand("input");
        outRadio = new JRadioButton(Messages.getString("dmxview.outputradio"),
                true);
        outRadio.setActionCommand("output");
        inOutRadioGroup.add(inRadio);
        inOutRadioGroup.add(outRadio);
        inOutRadioPanel.add(inRadio);
        inOutRadioPanel.add(outRadio);
        this.dmxPanel.add(inOutRadioPanel, BorderLayout.NORTH);

        final TableCellRenderer headerCellRenderer = new DefaultTableCellRenderer() {
            private static final long serialVersionUID = -7628430686172752221L;

            @Override
            public Component getTableCellRendererComponent(final JTable table,
                    final Object value, final boolean isSelected,
                    final boolean hasFocus, final int row, final int column) {
                super.getTableCellRendererComponent(table, value, isSelected,
                        hasFocus, row, column);
                setHorizontalAlignment(SwingConstants.CENTER);
                setFont(getFont().deriveFont(getFont().getStyle() ^ Font.BOLD));
                if ((row == 0 && column == -1) || (row == -1 && column == 0)) {
                    setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1,
                            Color.LIGHT_GRAY));
                } else if (row == -1) {
                    setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1,
                            Color.LIGHT_GRAY));
                } else if (column == -1) {
                    setBorder(BorderFactory.createMatteBorder(0, 1, 1, 1,
                            Color.LIGHT_GRAY));
                }

                setBackground(new Color(230, 230, 230));
                return this;
            }
        };

        final JTable dmxTable = new JTable(this.dmxTableModel);
        dmxTable.setEnabled(false);
        dmxTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        dmxTable.getTableHeader().setReorderingAllowed(false);
        dmxTable.getTableHeader().setResizingAllowed(false);
        dmxTable.setGridColor(Color.LIGHT_GRAY);
        dmxTable.getTableHeader().setPreferredSize(
                new Dimension(dmxTableModel.getColumnCount() * cellWidth,
                        cellHeight));
        dmxTable.setRowHeight(cellHeight);

        dmxTable.getTableHeader().setDefaultRenderer(headerCellRenderer);

        dmxTable.setDefaultRenderer(Integer.class,
                new DefaultTableCellRenderer() {
                    private static final long serialVersionUID = 8556900685268227709L;

                    @Override
                    public Component getTableCellRendererComponent(
                            final JTable table, final Object value,
                            final boolean isSelected, final boolean hasFocus,
                            final int row, final int column) {
                        super.getTableCellRendererComponent(table, value,
                                isSelected, hasFocus, row, column);
                        int nonBlueLevel = 255 - ((Integer) value).intValue() * 4 / 5;
                        setBackground(new Color(nonBlueLevel, nonBlueLevel, 255));
                        int channel = row * table.getColumnCount() + column + 1;
                        int percentValue = (int) Math.ceil(((Integer) value)
                                .intValue() * 100 / 255.0);
                        setToolTipText(MessageFormat.format(
                                Messages.getString("dmxview.tooltipmessage"),
                                channel, value, percentValue));
                        setHorizontalAlignment(SwingConstants.CENTER);
                        return this;
                    }
                });

        Enumeration<TableColumn> columns = dmxTable.getColumnModel()
                .getColumns();
        while (columns.hasMoreElements()) {
            TableColumn column = columns.nextElement();
            column.setPreferredWidth(cellWidth);
        }

        JList dmxTableRowHeader = new JList(this.dmxTableModel.getRowHeaders());
        dmxTableRowHeader.setFixedCellHeight(cellHeight);
        dmxTableRowHeader.setFixedCellWidth(cellWidth);
        dmxTableRowHeader.setCellRenderer(new DefaultListCellRenderer() {
            private static final long serialVersionUID = -6533501111809246770L;

            @Override
            public Component getListCellRendererComponent(final JList list,
                    final Object value, final int index,
                    final boolean isSelected, final boolean cellHasFocus) {
                Component c = headerCellRenderer.getTableCellRendererComponent(
                        dmxTable, value, isSelected, cellHasFocus, index, -1);
                return c;
            }
        });

        JScrollPane scrollPane = new JScrollPane(dmxTable);
        scrollPane.setRowHeaderView(dmxTableRowHeader);
        scrollPane.setPreferredSize(new Dimension(200, 200));
        this.dmxPanel.add(scrollPane, BorderLayout.CENTER);
    }

    /**
     * Add an action listener to in and out radio buttons.
     *
     * @param actionListener
     *            the listener to add
     */
    public void addInOutRadioActionListener(final ActionListener actionListener) {
        inRadio.addActionListener(actionListener);
        outRadio.addActionListener(actionListener);
    }

    /**
     * Remove an action listener from in and out radion buttons.
     *
     * @param actionListener
     *            the listener to remove
     */
    public void removeInOutRadioActionListener(
            final ActionListener actionListener) {
        inRadio.removeActionListener(actionListener);
        outRadio.removeActionListener(actionListener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("dmxview.title");
    }

    /**
     * Returns the DMX panel of the view.
     *
     * @return the DMX panel
     */
    @Override
    public JComponent getViewComponent() {
        return this.dmxPanel;
    }
} public class LogsLineView implements ViewInterface {

    private final RemoteModel remoteModel;

    private final JPanel logsLinePanel = new JPanel();

    private JLabel logLabel;

    /**
     * The constructor of the class.
     *
     * @param remoteModel
     *            the model associated with this view
     */
    public LogsLineView(final RemoteModel remoteModel) {
        this.remoteModel = remoteModel;
        this.logsLinePanel.setBorder(BorderFactory.createEtchedBorder());
        this.logLabel = new JLabel(LogsViewHelper.DEFAULT_TEXT);
        this.logsLinePanel.add(logLabel);
        this.initRemoteModelListener();
    }

    private void initRemoteModelListener() {
        this.remoteModel.getLogsListModel().addListDataListener(
                new ListDataListener() {

                    @Override
                    public void intervalRemoved(final ListDataEvent e) {
                        updateLabel();
                    }

                    @Override
                    public void intervalAdded(final ListDataEvent e) {
                        updateLabel();
                    }

                    @Override
                    public void contentsChanged(final ListDataEvent e) {
                        updateLabel();
                    }
                });

    }

    /**
     * Returns the log field.
     *
     * @return the log field of the view
     */
    @Override
    public JComponent getViewComponent() {
        return this.logsLinePanel;
    }

    /**
     * Update the label according to the current LogRecord.
     */
    private void updateLabel() {
        if (remoteModel.getLogsListModel().getSize() > 0) {
            final LogRecord logRecord = (LogRecord) remoteModel
                    .getLogsListModel().getElementAt(
                            remoteModel.getLogsListModel().getSize() - 1);
            LogsViewHelper.LOG_DECORATOR.update(logLabel, logRecord);
        } else {
            logLabel.setText(LogsViewHelper.DEFAULT_TEXT);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("logsview.title");
    }
} public class LogsView implements ViewInterface {

    private RemoteModel remoteModel;
    private JList logsList;

    private final GridBagLayout layout = new GridBagLayout();
    private final GridBagConstraints constraints = new GridBagConstraints();

    private final JPanel logsPanel = new JPanel();
    private final JButton clearLogsButton;

    /**
     * The constructor of the Logs View.
     *
     * @param remoteModel
     *            the RemoteModel used by the view
     */
    public LogsView(final RemoteModel remoteModel) {
        this.remoteModel = remoteModel;

        this.logsPanel.setLayout(this.layout);

        this.logsList = new JList(this.remoteModel.getLogsListModel());
        this.logsList.getModel().addListDataListener(new ListDataListener() {
            @Override
            public void intervalAdded(final ListDataEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        logsList.ensureIndexIsVisible(e.getIndex0());
                    }
                });
            }

            @Override
            public void intervalRemoved(final ListDataEvent e) {
            }

            @Override
            public void contentsChanged(final ListDataEvent e) {
            }
        });
        this.logsList.setCellRenderer(new ListCellRenderer() {

            @Override
            public Component getListCellRendererComponent(final JList list,
                    final Object value, final int index,
                    final boolean isSelected, final boolean cellHasFocus) {
                return LogsViewHelper.createLogsLabel((LogRecord) value);
            }
        });

        JScrollPane scrollPane = new JScrollPane(logsList);
        scrollPane.setMinimumSize(new Dimension(200, 100));
        scrollPane
                .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        this.constraints.fill = GridBagConstraints.BOTH;
        this.constraints.gridy = 0;
        this.constraints.weightx = 1;
        this.constraints.weighty = 1;
        this.logsPanel.add(scrollPane, this.constraints);

        this.clearLogsButton = new JButton(
                Messages.getString("logsview.clear"),
                new ImageIcon(
                        Elios.class
                                .getResource("/net/eliosoft/elios/gui/views/edit-clear.png"))); //$NON-NLS-1$
        this.constraints.fill = GridBagConstraints.NONE;
        this.constraints.gridy = 1;
        this.constraints.weightx = 0;
        this.constraints.weighty = 0;
        logsPanel.add(this.clearLogsButton, this.constraints);
    }

    /**
     * Returns the logs panel of the view.
     *
     * @return the logs panel
     */
    @Override
    public JComponent getViewComponent() {
        return this.logsPanel;
    }

    /**
     * Adds an action listener to the clear logs button.
     *
     * @param listener
     *            the listener to add
     */
    public void addClearLogsButtonListener(final ActionListener listener) {
        this.clearLogsButton.addActionListener(listener);
    }

    /**
     * Removes an action listener to the clear logs button.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeClearLogsButtonListener(final ActionListener listener) {
        this.clearLogsButton.removeActionListener(listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("logsview.title");
    }
} public final class Messages {
    /** bundle full qualified name. **/
    private static final String BUNDLE_NAME = "net.eliosoft.elios.gui.views.messages"; //$NON-NLS-1$

    private static final Logger LOGGER = LoggersManager.getInstance()
            .getLogger(Messages.class.getCanonicalName());

    /** bundle instance. **/
    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
            .getBundle(BUNDLE_NAME);

    /**
     * Ensure that no object can be create.
     */
    private Messages() {
        // nothing
    }

    /**
     * Returns the localized text of the given key if the resource is found,
     * returns !key! (key is the argument) otherwise.
     *
     * @param key
     *            string that identified a localized text
     * @return the localized text or !key! is the resource is not found
     */
    public static String getString(final String key) {
        try {
            return RESOURCE_BUNDLE.getString(key);
        } catch (MissingResourceException e) {
            return fallbackTranslation(key);
        }
    }

    /**
     * Returns the localized text of the given key if the resource is found or
     * throws a MissingResourceException.
     *
     * @param key
     *            string that identified a localized text
     * @return the localized text
     * @throws MissingResourceException
     *             if the resource is not found
     */
    private static String internalGetString(final String key)
            throws MissingResourceException {
        return RESOURCE_BUNDLE.getString(key);
    }

    /**
     * Returns a formatted localized string identified by the given key. The
     * string is populated according to {@link MessageFormat}.
     *
     * @param key
     *            string that identified a localized text
     * @param objects
     *            variable arguments of Object that will used to populated the
     *            string
     * @return the populated localized text, returns !key! (key is the argument)
     *         otherwise.
     */
    public static String getString(final String key, final Object... objects) {
        try {
            return new MessageFormat(internalGetString(key)).format(objects);
        } catch (MissingResourceException e) {
            return fallbackTranslation(key);
        }
    }

    /**
     * Returns the given string between "!".
     *
     * @param key
     *            a string
     * @return the given string between "!"
     */
    private static String fallbackTranslation(final String key) {
        LOGGER.warning("missing i18n key [" + key + "]");
        return '!' + key + '!';
    }
} public class PrefsView implements ViewInterface {

    private RemoteModel remoteModel;

    private final JPanel prefsPanel;

    private JButton cancelButton;
    private JButton saveButton;

    private JSpinner inPortSpinner;
    private JSpinner outPortSpinner;

    private JComboBox broadcastAddressCombo;

    private JSpinner httpPortSpinner;

    private JCheckBox enableHttpServerCheckBox;

    private JComboBox langComboBox;

    private LocaleComboBoxModel localeModel;

    private UpdateModel updateModel;

    /**
     * The Constructor of the view.
     *
     * @param remoteModel
     *            the model associated to the view
     * @param localeModel
     *            the associated {@link LocaleComboBoxModel}
     * @param updateModel
     *            the associated {@link UpdateModel}
     */
    public PrefsView(final RemoteModel remoteModel,
            final LocaleComboBoxModel localeModel, final UpdateModel updateModel) {
        this.remoteModel = remoteModel;
        this.localeModel = localeModel;
        this.updateModel = updateModel;

        prefsPanel = new JPanel();
        prefsPanel.setLayout(new BorderLayout());

        JPanel globalPane = new JPanel(new GridLayout(4, 0));

        // general panel
        globalPane.add(createGeneralPane());

        // artnet panel
        globalPane.add(createArtNetServerPane());

        // http server panel
        globalPane.add(createHttpServerPane());

        // add a glue to have a so beautiful fullscreen layout
        globalPane.add(Box.createVerticalGlue());

        prefsPanel.add(globalPane, BorderLayout.NORTH);

        // button panel
        prefsPanel.add(createButtonPane(), BorderLayout.SOUTH);
    }

    private Component createButtonPane() {

        JPanel buttonPanel = new JPanel();

        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));

        buttonPanel.add(Box.createGlue());

        // cancel / save
        this.cancelButton = new JButton(Messages.getString("prefsview.cancel")); //$NON-NLS-1$
        this.cancelButton.setIcon(new ImageIcon(Elios.class
                .getResource("/net/eliosoft/elios/gui/views/edit-undo.png")));
        buttonPanel.add(this.cancelButton);

        buttonPanel.add(Box.createHorizontalStrut(5));

        this.saveButton = new JButton(Messages.getString("prefsview.save")); //$NON-NLS-1$
        buttonPanel.add(this.saveButton);
        this.saveButton
                .setIcon(new ImageIcon(
                        Elios.class
                                .getResource("/net/eliosoft/elios/gui/views/document-save.png")));

        return buttonPanel;
    }

    private JPanel createArtNetServerPane() {
        JPanel serverPrefPanel = new JPanel();
        serverPrefPanel.setName(Messages.getString("prefsview.artnetserver")); //$NON-NLS-1$
        serverPrefPanel.setLayout(new GridBagLayout());
        serverPrefPanel.setBorder(BorderFactory.createTitledBorder(Messages
                .getString("prefsview.artnetserver"))); //$NON-NLS-1$
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.WEST;
        constraints.weightx = 1;

        // broadcast
        constraints.gridx = 0;
        constraints.gridy = 0;
        JLabel broadcastAddressLabel = new JLabel(
                Messages.getString("prefsview.broadcastaddress")); //$NON-NLS-1$
        serverPrefPanel.add(broadcastAddressLabel, constraints);
        broadcastAddressLabel.setLabelFor(this.broadcastAddressCombo);

        constraints.gridx = 1;
        constraints.gridy = 0;
        this.broadcastAddressCombo = new JComboBox(
                this.remoteModel.getBroadcastAddressComboModel());
        serverPrefPanel.add(this.broadcastAddressCombo, constraints);

        // in port
        constraints.gridx = 0;
        constraints.gridy = 1;
        JLabel inPortLabel = new JLabel(Messages.getString("prefsview.port.in")); //$NON-NLS-1$
        serverPrefPanel.add(inPortLabel, constraints);

        constraints.gridx = 1;
        constraints.gridy = 1;
        this.inPortSpinner = new JSpinner(
                this.remoteModel.getInPortSpinnerModel());
        inPortLabel.setLabelFor(this.inPortSpinner);
        serverPrefPanel.add(this.inPortSpinner, constraints);

        // out port
        constraints.gridx = 0;
        constraints.gridy = 2;
        JLabel outPortLabel = new JLabel(
                Messages.getString("prefsview.port.out")); //$NON-NLS-1$
        serverPrefPanel.add(outPortLabel, constraints);

        constraints.gridx = 1;
        constraints.gridy = 2;
        this.outPortSpinner = new JSpinner(
                this.remoteModel.getOutPortSpinnerModel());
        outPortLabel.setLabelFor(this.outPortSpinner);
        serverPrefPanel.add(this.outPortSpinner, constraints);

        return serverPrefPanel;
    }

    /**
     * Creates a panel for the general preferences.
     *
     * @return a panel containing input elements to set general preferences
     */
    private JPanel createGeneralPane() {
        final JPanel generalPrefsPanel = new JPanel();
        generalPrefsPanel.setBorder(BorderFactory.createTitledBorder(Messages
                .getString("prefsview.general"))); //$NON-NLS-1$
        generalPrefsPanel.setLayout(new GridBagLayout());
        GridBagConstraints constraints = new GridBagConstraints();

        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.WEST;
        constraints.weightx = 1;

        this.langComboBox = new JComboBox(localeModel);

        langComboBox.setRenderer(new DefaultListCellRenderer() {
            /**
             * serial UID.
             */
            private static final long serialVersionUID = -8372082223569889802L;

            @Override
            public Component getListCellRendererComponent(final JList list,
                    final Object value, final int index,
                    final boolean isSelected, final boolean cellHasFocus) {

                super.getListCellRendererComponent(list, value, index,
                        isSelected, cellHasFocus);
                setText(Messages
                        .getString("ui.lang." + ((Locale) value).getLanguage())); //$NON-NLS-1$
                return this;
            }
        });

        // lang chooser
        constraints.gridx = 0;
        constraints.gridy = 0;
        JLabel langComboLabel = new JLabel(Messages.getString("prefsview.lang")); //$NON-NLS-1$
        generalPrefsPanel.add(langComboLabel, constraints);

        constraints.gridx = 1;
        constraints.gridy = 0;
        langComboLabel.setLabelFor(this.langComboBox);
        generalPrefsPanel.add(this.langComboBox, constraints);

        // update chooser
        UpdateFrequencyChooserView view = new UpdateFrequencyChooserView(
                updateModel);
        JComponent viewComponent = view.getViewComponent();

        constraints.gridx = 0;
        constraints.gridy = 1;
        JLabel updateViewLabel = new JLabel(view.getLocalizedTitle());
        generalPrefsPanel.add(updateViewLabel, constraints);
        langComboLabel.setLabelFor(viewComponent);
        constraints.gridx = 1;
        constraints.gridy = 1;

        generalPrefsPanel.add(viewComponent, constraints);

        return generalPrefsPanel;
    }

    /**
     * Creates a panel for the http server preferences.
     *
     * @return a panel containing input elements to set http server preferences
     */
    private JPanel createHttpServerPane() {
        final JPanel httpServerPrefsPanel = new JPanel();
        httpServerPrefsPanel.setBorder(BorderFactory
                .createTitledBorder("HTTP Server"));
        httpServerPrefsPanel.setLayout(new GridBagLayout());

        GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.WEST;
        constraints.weightx = 1;

        // http server started
        constraints.gridx = 0;
        constraints.gridy = 0;
        this.enableHttpServerCheckBox = new JCheckBox(
                Messages.getString("prefsview.httpserver"), this.remoteModel.isHttpServerEnabled()); //$NON-NLS-1$
        httpServerPrefsPanel.add(this.enableHttpServerCheckBox, constraints);

        // http port
        constraints.gridx = 0;
        constraints.gridy = 1;
        JLabel httpPortLabel = new JLabel(
                Messages.getString("prefsview.port.http")); //$NON-NLS-1$
        httpServerPrefsPanel.add(httpPortLabel, constraints);

        constraints.gridx = 1;
        constraints.gridy = 1;
        this.httpPortSpinner = new JSpinner(
                this.remoteModel.getHttpPortSpinnerModel());
        httpServerPrefsPanel.add(this.httpPortSpinner, constraints);

        return httpServerPrefsPanel;
    }

    /**
     * Returns the preferences panel component.
     *
     * @return the panel Component
     */
    @Override
    public JComponent getViewComponent() {
        return this.prefsPanel;
    }

    /**
     * Adds an Action Listener to the Start ArtNet Button.
     *
     * @param actionListener
     *            the listener to add to the button
     */
    public void addCancelButtonListener(final ActionListener actionListener) {
        this.cancelButton.addActionListener(actionListener);
    }

    /**
     * Removes an Action Listener to the Start ArtNet Button.
     *
     * @param actionListener
     *            the listener to remove to the button
     */
    public void removeStartArtNetButtonListener(
            final ActionListener actionListener) {
        this.cancelButton.removeActionListener(actionListener);
    }

    /**
     * Adds an Action Listener to the Stop ArtNet Button.
     *
     * @param actionListener
     *            the listener to add to the button
     */
    public void addSaveButtonListener(final ActionListener actionListener) {
        this.saveButton.addActionListener(actionListener);
    }

    /**
     * Removes an Action Listener to the Stop ArtNet Button.
     *
     * @param actionListener
     *            the listener to remove to the button
     */
    public void removeStopArtNetButtonListener(
            final ActionListener actionListener) {
        this.saveButton.removeActionListener(actionListener);
    }

    /**
     * Add an Action Listener to the Enable Http server checkbox.
     *
     * @param actionListener
     *            the listener to add to the checkbox
     */
    public void addEnableHttpServerCheckBoxListener(
            final ActionListener actionListener) {
        this.enableHttpServerCheckBox.addActionListener(actionListener);
    }

    /**
     * Removes an Action Listener to the Enable Http server checkbox.
     *
     * @param actionListener
     *            the listener to remove to the checkbox
     */
    public void removeEnableHttpServerCheckBoxListener(
            final ActionListener actionListener) {
        this.enableHttpServerCheckBox.removeActionListener(actionListener);
    }

    /**
     * Add a {@link java.awt.event.ActionListener} to the lang combobox.
     *
     * @param l
     *            the listener to add to the lang combobox
     */
    public void addLangComboListener(final ActionListener l) {
        this.langComboBox.addActionListener(l);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("prefsview.title"); //$NON-NLS-1$
    }
} public class RemoteView implements ViewInterface {

    private final RemoteModel remoteModel;

    private final GridBagLayout layout = new GridBagLayout();
    private final GridBagConstraints constraints = new GridBagConstraints();

    private final JPanel remotePanel = new JPanel();
    private final JTextField commandLineTextField = new JTextField();

    private final List<JButton> valueButtonsList = new ArrayList<JButton>();
    private final List<Character> valuesList = new ArrayList<Character>();
    private final JButton enterButton;
    private final JButton resetButton;
    private final JButton delButton;

    /**
     * The default constructor of the remote view.
     *
     * @param remoteModel
     *            the model associated to the view
     */
    public RemoteView(final RemoteModel remoteModel) {
        this.remoteModel = remoteModel;
        this.remoteModel
                .addRemoteModelChangedListener(new RemoteModelListener() {
                    @Override
                    public void commandLineValueChanged(
                            final CommandLineValueChangedEvent event) {
                        commandLineTextField.setText(event.getCommand());
                        if (event.getCommand().isEmpty()) {
                            delButton.setEnabled(false);
                            resetButton.setEnabled(false);
                        } else {
                            delButton.setEnabled(true);
                            resetButton.setEnabled(true);
                        }
                    }

                    @Override
                    public void artNetStarted(final ArtNetStartedEvent event) {
                        enterButton.setEnabled(true);
                    }

                    @Override
                    public void artNetStopped(final ArtNetStoppedEvent event) {
                        enterButton.setEnabled(false);
                    }

                    @Override
                    public void httpStarted(final HttpStartedEvent event) {
                    }

                    @Override
                    public void httpStopped(final HttpStoppedEvent event) {
                    }

                    @Override
                    public void additiveModeValueChanged(
                            final AdditiveModeValueChangedEvent event) {
                    }
                });

        this.remotePanel.setLayout(layout);

        this.commandLineTextField.setEditable(false);
        this.commandLineTextField.setFocusable(false);
        this.constraints.fill = GridBagConstraints.BOTH;
        this.constraints.weightx = 1;
        this.constraints.weighty = 1;
        this.constraints.gridy = 0;
        this.constraints.gridwidth = 4;
        this.remotePanel.add(this.commandLineTextField, this.constraints);

        this.addValueButton('1', 1, 1, true);
        this.addValueButton('2', 1, 1, true);
        this.addValueButton('3', 1, 1, true);
        this.addValueButton('+', 1, 1, true);
        this.addValueButton('4', 2, 1, true);
        this.addValueButton('5', 2, 1, true);
        this.addValueButton('6', 2, 1, true);
        this.addValueButton('-', 2, 1, true);
        this.addValueButton('7', 3, 1, true);
        this.addValueButton('8', 3, 1, true);
        this.addValueButton('9', 3, 1, true);
        this.addValueButton('/', 3, 1, true);
        this.addValueButton(';', 4, 1, true);
        this.addValueButton('0', 4, 1, true);
        this.addValueButton('@', 4, 1, true);
        this.addValueButton('F', 4, 1, true);
        this.delButton = this.addButton(
                Messages.getString("remoteview.delete"), 5, 1, false); //$NON-NLS-1$
        this.enterButton = this.addButton(
                Messages.getString("remoteview.enter"), 5, 1, false); //$NON-NLS-1$
        this.resetButton = this.addButton(
                Messages.getString("remoteview.reset"), 5, 1, false); //$NON-NLS-1$
        this.addValueButton('D', 5, 1, true);
    }

    /**
     * Gets the remote panel.
     *
     * @return the panel
     */

    @Override
    public JComponent getViewComponent() {
        return this.remotePanel;
    }

    /**
     * Sets text of the command line field.
     *
     * @param value
     *            the text to set
     */
    public void setCommandLineFieldValue(final String value) {
        this.commandLineTextField.setText(value);
    }

    /**
     * Adds an element to the list of listeners of the remote panel.
     *
     * @param listener
     *            the listener to add
     */
    public void addRemotePanelKeyListener(final KeyListener listener) {
        this.remotePanel.addKeyListener(listener);
    }

    /**
     * Removes an element from the list of listeners of the remote panel.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeRemotePanelKeyListener(final KeyListener listener) {
        this.remotePanel.removeKeyListener(listener);
    }

    /**
     * Adds an element to the list of listeners of the value buttons.
     *
     * @param listener
     *            the listener to add
     */
    public void addValueButtonsListener(final ActionListener listener) {
        for (JButton button : valueButtonsList) {
            button.addActionListener(listener);
        }
    }

    /**
     * Removes an element from the list of listeners of the value buttons.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeValueButtonsListener(final ActionListener listener) {
        for (JButton button : valueButtonsList) {
            button.removeActionListener(listener);
        }
    }

    /**
     * Adds an element to the list of listeners of the del button.
     *
     * @param actionListener
     *            the listener to add
     */
    public void addDelButtonListener(final ActionListener actionListener) {
        this.delButton.addActionListener(actionListener);
    }

    /**
     * Removes an element from the list of listeners of the del button.
     *
     * @param actionListener
     *            the listener to remove
     */
    public void removeDelButtonListener(final ActionListener actionListener) {
        this.delButton.removeActionListener(actionListener);
    }

    /**
     * Adds an element to the list of listeners of the enter button.
     *
     * @param actionListener
     *            the listener to add
     */
    public void addEnterButtonListener(final ActionListener actionListener) {
        this.enterButton.addActionListener(actionListener);
    }

    /**
     * Removes an element from the list of listeners of the enter button.
     *
     * @param actionListener
     *            the listener to remove
     */
    public void removeEnterButtonListener(final ActionListener actionListener) {
        this.enterButton.removeActionListener(actionListener);
    }

    /**
     * Adds an element to the list of listeners of the reset button.
     *
     * @param actionListener
     *            the listener to add
     */
    public void addResetButtonListener(final ActionListener actionListener) {
        this.resetButton.addActionListener(actionListener);
    }

    /**
     * Removes an element from the list of listeners of the reset button.
     *
     * @param actionListener
     *            the listener to remove
     */
    public void removeResetButtonListener(final ActionListener actionListener) {
        this.resetButton.removeActionListener(actionListener);
    }

    /**
     * Gets the input map of the remote panel.
     *
     * @return the input map
     */
    public InputMap getRemotePanelInputMap() {
        return this.remotePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
    }

    /**
     * Gets the action map of the remote panel.
     *
     * @return the action map
     */
    public ActionMap getRemotePanelActionMap() {
        return this.remotePanel.getActionMap();
    }

    /**
     * Determines whether the enter button is enabled.
     *
     * @return true if it enabled or false it is disabled
     */
    public boolean isEnterButtonEnabled() {
        return this.enterButton.isEnabled();
    }

    /**
     * Determines whether the reset button is enabled.
     *
     * @return true if it enabled or false it is disabled
     */
    public boolean isResetButtonEnabled() {
        return this.resetButton.isEnabled();
    }

    /**
     * Determines whether the del button is enabled.
     *
     * @return true if it enabled or false it is disabled
     */
    public boolean isDelButtonEnabled() {
        return this.delButton.isEnabled();
    }

    /**
     * Gets the list of values on the buttons.
     *
     * @return the values list
     */
    public List<Character> getValuesList() {
        return this.valuesList;
    }

    private void addValueButton(final Character c, final int gridY,
            final int gridWidth, final boolean enabled) {
        JButton button = addButton(c.toString(), gridY, gridWidth, enabled);
        valuesList.add(c);
        valueButtonsList.add(button);
    }

    private JButton addButton(final String text, final int gridY,
            final int gridWidth, final boolean enabled) {
        JButton button = new JButton(text);
        button.setEnabled(enabled);
        button.setFocusable(false);

        this.constraints.gridy = gridY;
        this.constraints.gridwidth = gridWidth;

        remotePanel.add(button, constraints);
        return button;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLocalizedTitle() {
        return Messages.getString("remoteview.title");
    }
} public interface ViewInterface {
    /**
     * get the component containing the view.
     *
     * @return the view component
     */
    JComponent getViewComponent();

    /**
     * Returns the localized title of the view.
     *
     * @return the localized title.
     */
    String getLocalizedTitle();
} enum State {
    RUNNING, SHUTTING_DOWN
} public class ArtNetServerManager {

    /**
     * The max number of DMX channels.
     */
    public static final int DMX_CHANNELS_COUNT = 512;

    private static ArtNetServerManager instance;

    /**
     * default value for ArtNet port.
     */
    public static final int DEFAULT_ARTNET_PORT = ArtNetServer.DEFAULT_PORT;

    private ArtNetServer artnetServer = null;
    private int inPort = ArtNetServerManager.DEFAULT_ARTNET_PORT;
    private int outPort = ArtNetServerManager.DEFAULT_ARTNET_PORT;
    private String broadcastAddress = ArtNetServer.DEFAULT_BROADCAST_IP;
    private int serverSubnet = 0;
    private int serverUniverse = 0;
    private int sequenceId = 0;
    private boolean additiveModeEnabled = false;

    private static final Pattern COMMANDLINE_PATTERN = Pattern
            .compile(
                    "^(((\\d{1,3})(/(\\d{1,3}))?)([\\+\\-]((\\d{1,3})(/(\\d{1,3}))?))*)(@(F|(D?)(\\d{1,3}))){0,1}$",
                    Pattern.CASE_INSENSITIVE);
    private static final Pattern CHANNEL_PATTERN = Pattern.compile(
            "([\\+\\-]?)(\\d{1,3})(/(\\d{1,3}))?", Pattern.CASE_INSENSITIVE);

    private static final int COMMAND_LINE_PATTERN_CHANNEL_GROUP = 1;
    private static final int COMMAND_LINE_PATTERN_AT_LEVEL_GROUP = 11;
    private static final int COMMAND_LINE_PATTERN_LEVEL_GROUP = 12;
    private static final int COMMAND_LINE_PATTERN_PERCENT_GROUP = 13;
    private static final int COMMAND_LINE_PATTERN_LEVEL_VALUE_GROUP = 14;

    private static final int CHANNEL_PATTERN_EXCEPT_GROUP = 1;
    private static final int CHANNEL_PATTERN_CHAN1_GROUP = 2;
    private static final int CHANNEL_PATTERN_CHAN2_GROUP = 4;

    private static final int MIN_CHANNEL_NUMBER = 1;
    private static final int MAX_CHANNEL_NUMBER = DMX_CHANNELS_COUNT;

    private static final int MAX_DMX_VALUE = 255;
    private static final int MAX_PERCENT_VALUE = 100;

    private static final int SUBNET_COUNT = 16;
    private static final int UNIVERSE_COUNT = 16;

    private byte[][][] outputDmxArrays = new byte[SUBNET_COUNT][UNIVERSE_COUNT][DMX_CHANNELS_COUNT];
    private byte[][][] inputDmxArrays = new byte[SUBNET_COUNT][UNIVERSE_COUNT][DMX_CHANNELS_COUNT];

    private final transient Logger logger = LoggersManager.getInstance()
            .getLogger(ArtNetServerManager.class.getName());

    private List<ArtNetServerManagerListener> artnetServerManagerChangedListeners = new ArrayList<ArtNetServerManagerListener>();

    private ArtNetServerManager() {
    }

    /**
     * get the singleton instance of the ArtnetServerManager.
     *
     * @return the instance
     */
    public static ArtNetServerManager getInstance() {
        if (ArtNetServerManager.instance == null) {
            ArtNetServerManager.instance = new ArtNetServerManager();
        }
        return ArtNetServerManager.instance;
    }

    /**
     * send a Dmx Command containing the value of the Dmx array over the
     * network.
     */
    public void sendDmxCommand() {
        ArtDmxPacket artDmxPacket = new ArtDmxPacket();
        artDmxPacket.setUniverse(this.serverSubnet, this.serverUniverse);
        artDmxPacket.setSequenceID(this.sequenceId % 255);
        artDmxPacket.setDMX(getCurrentOutputDmxArray(), DMX_CHANNELS_COUNT);
        this.artnetServer.broadcastPacket(artDmxPacket);
        logger.info("broadcast DMX packet sent");

        this.sequenceId++;
    }

    /**
     * Starts the ArtNet Server.
     *
     * @throws ArtNetException
     *             if a server is already running
     * @throws SocketException
     *             if there is a problem with the server socket
     */
    public void startArtNet() throws SocketException, ArtNetException {
        this.artnetServer = new ArtNetServer(this.inPort, this.outPort);
        this.artnetServer.setBroadcastAddress(this.broadcastAddress);
        this.artnetServer.start();
        initArtNetReceiver();

        logger.info("ArtNet Started (in:" + this.inPort + ", out:"
                + this.outPort + ")");
    }

    /**
     * Stops the ArtNet Server.
     */
    public void stopArtNet() {
        if (this.artnetServer != null) {
            this.artnetServer.stop();
            this.artnetServer = null;
            logger.info("ArtNet Stopped");
        }
    }

    /**
     * Process the value of the command line.
     *
     * @param commandLine
     *            the command line to process
     * @throws BadSyntaxException
     *             thrown when the command line has a bad syntax
     */
    public void processCommandLine(final String commandLine)
            throws BadSyntaxException {
        String[] commands = commandLine.split(";");
        HashMap<Integer, Byte> valuesToPush = new HashMap<Integer, Byte>();

        for (String command : commands) {
            Matcher commandLineMatcher = COMMANDLINE_PATTERN.matcher(command);
            if (commandLineMatcher.find()) {
                Matcher channelMatcher = CHANNEL_PATTERN
                        .matcher(commandLineMatcher
                                .group(COMMAND_LINE_PATTERN_CHANNEL_GROUP));
                HashSet<Integer> channels = new HashSet<Integer>();
                while (channelMatcher.find()) {
                    boolean except = (channelMatcher.group(
                            CHANNEL_PATTERN_EXCEPT_GROUP).compareTo("-") == 0);
                    int chan1 = Integer.parseInt(channelMatcher
                            .group(CHANNEL_PATTERN_CHAN1_GROUP));
                    checkChannelNumber(chan1);
                    int chan2 = chan1;
                    if (channelMatcher.group(CHANNEL_PATTERN_CHAN2_GROUP) != null) {
                        chan2 = Integer.parseInt(channelMatcher
                                .group(CHANNEL_PATTERN_CHAN2_GROUP));
                        checkChannelNumber(chan2);
                    }
                    for (int j = Math.min(chan1, chan2); j <= Math.max(chan1,
                            chan2); j++) {
                        if (except) {
                            channels.remove(j);
                        } else {
                            channels.add(j);
                        }
                    }
                }

                boolean hasLevel = !(commandLineMatcher
                        .group(COMMAND_LINE_PATTERN_AT_LEVEL_GROUP) == null || commandLineMatcher
                        .group(COMMAND_LINE_PATTERN_AT_LEVEL_GROUP).isEmpty());
                boolean percent = false;
                int value = MAX_DMX_VALUE;

                if (hasLevel) {
                    percent = (commandLineMatcher
                            .group(COMMAND_LINE_PATTERN_PERCENT_GROUP) == null || commandLineMatcher
                            .group(COMMAND_LINE_PATTERN_PERCENT_GROUP)
                            .isEmpty());
                    value = (commandLineMatcher
                            .group(COMMAND_LINE_PATTERN_LEVEL_GROUP)
                            .toUpperCase().compareTo("F") == 0) ? MAX_PERCENT_VALUE
                            : Integer
                                    .parseInt(commandLineMatcher
                                            .group(COMMAND_LINE_PATTERN_LEVEL_VALUE_GROUP));
                }

                if ((percent && value > MAX_PERCENT_VALUE)
                        || value > MAX_DMX_VALUE) {
                    throw new BadSyntaxException();
                }

                // adding values to HashMap of values to push

                byte dmxValue = (byte) value;
                if (percent) {
                    dmxValue = (byte) (value / 100.0 * 255);
                }
                for (int channel : channels) {
                    valuesToPush.put(channel - 1, dmxValue);
                }
            } else {
                throw new BadSyntaxException();
            }
        }
        pushValuesInCurrentOutputDmxArray(valuesToPush);
        logger.info("Command line parsed : " + commandLine);

    }

    private static void checkChannelNumber(final int channel)
            throws BadSyntaxException {
        if (channel < MIN_CHANNEL_NUMBER || channel > MAX_CHANNEL_NUMBER) {
            throw new BadSyntaxException();
        }
    }

    private void resetCurrentOutputDmxArray() {
        setCurrentOutputDmxArray(new byte[DMX_CHANNELS_COUNT]);
    }

    private void pushValuesInCurrentOutputDmxArray(
            final HashMap<Integer, Byte> valuesMap) {
        if (!additiveModeEnabled) {
            resetCurrentOutputDmxArray();
        }
        for (Entry<Integer, Byte> value : valuesMap.entrySet()) {
            getCurrentOutputDmxArray()[value.getKey()] = value.getValue();
        }
    }

    private void initArtNetReceiver() {
        this.artnetServer.addListener(new ArtNetServerListener() {
            @Override
            public void artNetPacketReceived(final ArtNetPacket artNetPacket) {
                switch (artNetPacket.getType()) {
                case ART_OUTPUT:
                    ArtDmxPacket artDmxPacket = (ArtDmxPacket) artNetPacket;
                    int subnet = artDmxPacket.getSubnetID();
                    int universe = artDmxPacket.getUniverseID();
                    System.arraycopy(artDmxPacket.getDmxData(), 0,
                            inputDmxArrays[subnet][universe], 0,
                            artDmxPacket.getNumChannels());
                    break;

                default:
                    break;
                }
            }

            @Override
            public void artNetServerStopped(final ArtNetServer artNetServer) {
            }

            @Override
            public void artNetServerStarted(final ArtNetServer artNetServer) {
            }

            @Override
            public void artNetPacketUnicasted(final ArtNetPacket artNetPacket) {
            }

            @Override
            public void artNetPacketBroadcasted(final ArtNetPacket artNetPacket) {
            }
        });

    }

    /**
     * set the broadcast address.
     *
     * @param broadcastAddress
     */
    public void setBroadcastAddress(final String broadcastAddress) {
        this.broadcastAddress = broadcastAddress;
        if (this.artnetServer != null) {
            this.artnetServer.setBroadcastAddress(broadcastAddress);
        }
    }

    /**
     * get the server output dmx array for a given subnet and universe.
     *
     * @param subnet
     *            the subnet to select
     * @param universe
     *            the universe to select
     * @return the array
     */
    public byte[] getOutputDmxArray(final int subnet, final int universe) {
        return outputDmxArrays[subnet][universe];
    }

    /**
     * get the server current output dmx array.
     *
     * @return the array
     */
    public byte[] getCurrentOutputDmxArray() {
        return getOutputDmxArray(this.serverSubnet, this.serverUniverse);
    }

    /**
     * set the server output dmx array for a given subnet and universe.
     *
     * @param subnet
     *            the subnet to select
     * @param universe
     *            the universe to select
     * @param dmxArray
     *            the value of the dmx array
     */
    public void setOutputDmxArray(final int subnet, final int universe,
            final byte[] dmxArray) {
        System.arraycopy(dmxArray, 0, outputDmxArrays[subnet][universe], 0,
                DMX_CHANNELS_COUNT);
    }

    /**
     * set the server current output dmx array.
     *
     * @param dmxArray
     *            the value of the dmx array
     */
    public void setCurrentOutputDmxArray(final byte[] dmxArray) {
        setOutputDmxArray(this.serverSubnet, this.serverUniverse, dmxArray);
    }

    /**
     * get the server input dmx array for a given subnet and universe.
     *
     * @param subnet
     *            the subnet to select
     * @param universe
     *            the universe to select
     * @return the array
     */
    public byte[] getInputDmxArray(final int subnet, final int universe) {
        return inputDmxArrays[subnet][universe];
    }

    /**
     * get the server input dmx array with current server subnet and universe
     * settings.
     *
     * @return the array
     */
    public byte[] getCurrentInputDmxArray() {
        return getInputDmxArray(this.serverSubnet, this.serverUniverse);
    }

    /**
     * set the in port of the server.
     *
     * @param inPort
     *            the value of the port
     */
    public void setInPort(final int inPort) {
        this.inPort = inPort;
    }

    /**
     * set the out port of the server.
     *
     * @param outPort
     *            the value of the port
     */
    public void setOutPort(final int outPort) {
        this.outPort = outPort;
    }

    /**
     * set the dmx subnet.
     *
     * @param subnet
     *            the value of the subnet
     */
    public void setSubnet(final int subnet) {
        this.serverSubnet = subnet;
        fireSubnetValueChanged();
    }

    /**
     * set the dmx universe.
     *
     * @param universe
     *            the value of the universe
     */
    public void setUniverse(final int universe) {
        this.serverUniverse = universe;
        fireUniverseValueChanged();
    }

    /**
     * Enable or disable the additive mode.
     *
     * @param additiveModeEnabled
     *            true to enable the additive mode, false to disable
     */
    public void setAdditiveModeEnabled(final boolean additiveModeEnabled) {
        this.additiveModeEnabled = additiveModeEnabled;
        fireAdditiveModeValueChanged();
    }

    /**
     * Returns the in port.
     *
     * @return the in port
     */
    public int getInPort() {
        return this.inPort;
    }

    /**
     * Returns the out port.
     *
     * @return the out port
     */
    public int getOutPort() {
        return this.outPort;
    }

    /**
     * Returns the dmx subnet.
     *
     * @return the dmx subnet
     */
    public int getSubnet() {
        return this.serverSubnet;
    }

    /**
     * Returns the dmx universe.
     *
     * @return the dmx universe
     */
    public int getUniverse() {
        return this.serverUniverse;
    }

    /**
     * Returns the status of the additive mode.
     *
     * @return the status of additive mode
     */
    public boolean isAdditiveModeEnabled() {
        return this.additiveModeEnabled;
    }

    /**
     * Adds an element to the list of listener of the artnet server manager.
     *
     * @param listener
     *            the listener to add
     */
    public void addArtNetServerManagerChangedListener(
            final ArtNetServerManagerListener listener) {
        this.artnetServerManagerChangedListeners.add(listener);
    }

    /**
     * Removes an element to the list of listener of the artnet server manager.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeArtNetServerManagerChangedListener(
            final ArtNetServerManagerListener listener) {
        this.artnetServerManagerChangedListeners.remove(listener);
    }

    private void fireSubnetValueChanged() {
        for (ArtNetServerManagerListener listener : this.artnetServerManagerChangedListeners) {
            SubnetValueChangedEvent e = new SubnetValueChangedEvent(
                    this.serverSubnet);
            listener.subnetValueChanged(e);
        }
    }

    private void fireUniverseValueChanged() {
        for (ArtNetServerManagerListener listener : this.artnetServerManagerChangedListeners) {
            UniverseValueChangedEvent e = new UniverseValueChangedEvent(
                    this.serverUniverse);
            listener.universeValueChanged(e);
        }
    }

    private void fireAdditiveModeValueChanged() {
        for (ArtNetServerManagerListener listener : this.artnetServerManagerChangedListeners) {
            AdditiveModeValueChangedEvent e = new AdditiveModeValueChangedEvent(
                    this.additiveModeEnabled);
            listener.additiveModeValueChanged(e);
        }
    }
} public class CuesManager {

    private final Logger logger = LoggersManager.getInstance().getLogger(
            CuesManager.class.getCanonicalName());

    private Map<String, Cue> cuesMap = new HashMap<String, Cue>();

    private static CuesManager instance;

    private List<CuesManagerListener> cuesManagerChangedListeners = new ArrayList<CuesManagerListener>();

    private CuesManager() {
    }

    /**
     * get the singleton instance of the CuesManager.
     *
     * @return the instance
     */
    public static CuesManager getInstance() {
        if (instance == null) {
            instance = new CuesManager();
        }
        return instance;
    }

    /**
     * add a cue in the manager.
     *
     * @param cue
     *            the cue to add to the manager
     */
    public void addCue(final Cue cue) {
        if (cuesMap.containsKey(cue.getName())) {
            throw new IllegalArgumentException(
                    "a cue with this name is already set");
        } else {
            logger.info("Cue [" + cue.getName() + "] added");
            cuesMap.put(cue.getName(), cue);
            fireCueAdded(cue.getName());
        }
    }

    /**
     * remove the requested cue.
     *
     * @param name
     *            the name of the cue to remove
     */
    public void removeCue(final String name) {
        logger.info("Cue [" + name + "] removed");
        cuesMap.remove(name);
        this.fireCueRemoved(name);
    }

    /**
     * get the cue with the requested name.
     *
     * @param name
     * @return the requested cue if found or null if no cue is found
     */
    public Cue getCue(final String name) {
        logger.info("Cue [" + name + "] loaded");
        return cuesMap.get(name);
    }

    /**
     * get all the cues of the manager.
     *
     * @return a list containing the cues
     */
    public List<Cue> getCues() {
        ArrayList<Cue> cuesList = new ArrayList<Cue>(cuesMap.values());
        Collections.sort(cuesList, new Comparator<Cue>() {

            @Override
            public int compare(final Cue c1, final Cue c2) {
                return c1.getName().compareTo(c2.getName());
            }
        });
        return cuesList;
    }

    /**
     * get an unused cue name.
     *
     * @return an unused cue name
     */
    public String getUnusedCueName() {
        int cueNumber = cuesMap.values().size();
        DecimalFormat decimalFormat = new DecimalFormat("000");
        String cueName;
        do {
            cueName = "Cue#" + decimalFormat.format(cueNumber);
            cueNumber++;
        } while (cuesMap.containsKey(cueName));
        return cueName;
    }

    /**
     * Adds an element to the list of listener of the cues manager.
     *
     * @param listener
     *            the listener to add
     */
    public void addCuesManagerChangedListener(final CuesManagerListener listener) {
        this.cuesManagerChangedListeners.add(listener);
    }

    /**
     * Removes an element to the list of listener of the cues manager.
     *
     * @param listener
     *            the listener to remove
     */
    public void removeCuesManagerChangedListener(
            final CuesManagerListener listener) {
        this.cuesManagerChangedListeners.remove(listener);
    }

    private void fireCueAdded(final String name) {
        for (CuesManagerListener listener : this.cuesManagerChangedListeners) {
            CueAddedEvent e = new CueAddedEvent(name);
            listener.cueAdded(e);
        }
    }

    private void fireCueRemoved(final String name) {
        for (CuesManagerListener listener : this.cuesManagerChangedListeners) {
            CueRemovedEvent e = new CueRemovedEvent(name);
            listener.cueRemoved(e);
        }
    }

    /**
     * Persists the current cues to the given {@link OutputStream}.
     *
     * @param stream
     *            the stream used to store the cues
     * @throws IOException
     *             if something goes wrong when persisting
     * @see CuesManager#load(InputStream)
     */
    public void persist(final OutputStream stream) throws IOException {
        stream.write(new Gson().toJson(cuesMap).getBytes());
    }

    /**
     * Loads cues from the given {@link InputStream}.
     *
     * @param stream
     *            {@link InputStream} from which the cues must be load
     * @see CuesManager#persist(OutputStream)
     */
    public void load(final InputStream stream) {
        cuesMap = new Gson().fromJson(new InputStreamReader(stream),
                new TypeToken<Map<String, Cue>>() {
                }.getType());
    }
} public class HttpServerManager {
    private static HttpServerManager instance;

    private final ResourceHttpHandler resourceHttpHandler = new ResourceHttpHandler();
    private final CommandLineHttpHandler commanLineHttpHandler = new CommandLineHttpHandler();
    private final ParamsHttpHandler paramsHttpHandler = new ParamsHttpHandler();
    private final DMXTableHttpHandler dmxTableHttpHandler = new DMXTableHttpHandler();
    private final CuesHttpHandler cuesHttpHandler = new CuesHttpHandler();

    /**
     * default value for http port.
     */
    public static final int DEFAULT_HTTP_PORT = 8080;

    private HttpServer httpServer = null;
    private int inPort = HttpServerManager.DEFAULT_HTTP_PORT;

    private final transient Logger logger = LoggersManager.getInstance()
            .getLogger(HttpServerManager.class.getName());

    private HttpServerManager() {
    }

    /**
     * get the singleton instance of the HttpServerManager.
     *
     * @return the instance
     */
    public static HttpServerManager getInstance() {
        if (HttpServerManager.instance == null) {
            HttpServerManager.instance = new HttpServerManager();
        }
        return HttpServerManager.instance;
    }

    /**
     * Starts the Http Server.
     *
     * @throws IOException
     *             if the server is unable to start
     */
    public void startHttp() throws IOException {
        this.initHttpServer();
        this.httpServer.start();
        logger.info("Http Started");
    }

    /**
     * Stops the Http Server.
     */
    public void stopHttp() {
        if (this.httpServer != null) {
            this.httpServer.stop(0);
            this.httpServer = null;
            logger.info("Http Stopped");
        }
    }

    /**
     * set the in port of the server.
     *
     * @param inPort
     *            the value of the port
     */
    public void setInPort(final int inPort) {
        this.inPort = inPort;
    }

    private void initHttpServer() throws IOException {
        this.httpServer = HttpServer.create(new InetSocketAddress(this.inPort),
                0);
        this.httpServer.createContext("/", this.resourceHttpHandler);
        this.httpServer.createContext("/data/commandLine",
                this.commanLineHttpHandler);
        this.httpServer.createContext("/data/params", this.paramsHttpHandler);
        this.httpServer.createContext("/data/dmxTable",
                this.dmxTableHttpHandler);
        this.httpServer.createContext("/data/cues", this.cuesHttpHandler);
    }

} public class ReleaseCode implements Comparable<ReleaseCode> {

    /** the identifier of the release. **/
    private String code;

    /** the development suffix. **/
    private static final String DEVELOPMENT_SUFFIX = "-dev";

    /** validation pattern. **/
    public static final Pattern VALIDATION_PATTERN = Pattern
            .compile("([0-9]\\.[0-9]*)(" + DEVELOPMENT_SUFFIX + ")*");

    /**
     * Constructs an empty release code.
     */
    ReleaseCode() {
        // nothing
    }

    /**
     * Constructs a {@link ReleaseCode} with the given code.
     *
     * @param code
     *            the code non null string
     */
    ReleaseCode(final String code) {
        if (code == null) {
            throw new IllegalArgumentException("The code could not be null");
        }
        if (!VALIDATION_PATTERN.matcher(code).matches()) {
            throw new IllegalArgumentException(
                    "The code must match the given pattern "
                            + VALIDATION_PATTERN.pattern());
        }

        this.code = code;
    }

    /**
     * Creates a fresh new {@link ReleaseCode}.
     *
     * @param code
     *            the code that identified the release
     * @return a newly create {@link ReleaseCode}
     */
    public static ReleaseCode create(final String code) {
        return new ReleaseCode(code);
    }

    /**
     * Returns the code.
     *
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * {@inheritDoc}
     *
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((code == null) ? 0 : code.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ReleaseCode other = (ReleaseCode) obj;
        if (code == null) {
            if (other.code != null) {
                return false;
            }
        } else if (!code.equals(other.code)) {
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     *
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    @Override
    public int compareTo(final ReleaseCode o) {
        Double d = Double.valueOf(extractCode(this) - extractCode(o));
        if (d > 0) {
            return 1;
        }
        if (d < 0) {
            return -1;
        }
        return 0;
    }

    /**
     * Extracts a {@link Double} representation of the code.
     *
     * @param rc
     *            a {@link ReleaseCode}
     * @return a {@link Double} representation of the code
     */
    private Double extractCode(final ReleaseCode rc) {
        Matcher matcher = VALIDATION_PATTERN.matcher(rc.code);
        matcher.matches(); // always true
        Double value = Double.valueOf(matcher.group(1));
        if (rc.isUnderDevelopment()) {
            value -= 0.00000001;
        }
        return value;
    }

    /**
     * Returns true if the current ReleaseCode is associated to a release under
     * development.
     *
     * @return true if the current ReleaseCode is associated to a release under
     *         development
     */
    public boolean isUnderDevelopment() {
        return code.endsWith(DEVELOPMENT_SUFFIX);
    }

    /**
     * Returns true if the current release code is older that the given one,
     * false otherwise.
     *
     * @param code
     *            a {@link ReleaseCode} instance
     * @return true if the current release code is older than the given one,
     *         false otherwise
     */
    public boolean before(final ReleaseCode code) {
        if (this.compareTo(code) == -1) {
            return true;
        }
        if (this.compareTo(code) == 0 && isUnderDevelopment()) {
            return true;
        }
        return false;
    }

    /**
     * Returns true if the current release code is newer than the given one,
     * false otherwise.
     *
     * @param code
     *            a {@link ReleaseCode} instance
     * @return true if the current release code is newer than the given one,
     *         false otherwise
     */
    public boolean after(final ReleaseCode code) {
        return !code.before(this);
    }
} public class ReleaseInformation {

    /** the release code. **/
    private ReleaseCode releaseCode;

    /** the release time. **/
    private long releaseTime;

    /** the URL of the download page. **/
    private URL downloadUrl;

    /** the URL of the release page. **/
    private URL releaseNoteUrl;

    /**
     * Constructs a {@link ReleaseInformation} with the given parameters.
     *
     * @param releaseCode
     *            the release code
     * @param downloadUrl
     *            the URL of the download page
     * @param releaseNoteUrl
     *            the URL of the release note
     * @param releaseDate
     *            the date of the release
     */
    public ReleaseInformation(final ReleaseCode releaseCode,
            final URL downloadUrl, final URL releaseNoteUrl,
            final Date releaseDate) {
        this.releaseCode = releaseCode;
        this.downloadUrl = downloadUrl;
        this.releaseTime = releaseDate.getTime();
        this.releaseNoteUrl = releaseNoteUrl;
    }

    /**
     * Package constructor for builder pattern.
     */
    ReleaseInformation() {
        // nothing
    }

    /**
     * Returns the release code.
     *
     * @return the release code
     */
    public ReleaseCode getReleaseCode() {
        return releaseCode;
    }

    /**
     * Sets the release code.
     *
     * @param releaseCode
     *            the release code
     */
    void setReleaseCode(final ReleaseCode releaseCode) {
        this.releaseCode = releaseCode;
    }

    /**
     * Sets the {@link URL} of the download page.
     *
     * @param downloadUrl
     *            a string representation of the URL
     * @throws MalformedURLException
     *             if the given string is not a valid {@link URL}.
     */
    void setDownloadUrl(final String downloadUrl) throws MalformedURLException {
        this.downloadUrl = URI.create(downloadUrl).toURL();
    }

    /**
     * Sets the {@link URL} of the release note page.
     *
     * @param releaseNoteUrl
     *            a string representation of the URL of the release note
     * @throws MalformedURLException
     */
    void setReleaseNoteUrl(final String releaseNoteUrl)
            throws MalformedURLException {
        this.releaseNoteUrl = URI.create(releaseNoteUrl).toURL();
    }

    void setReleaseTime(final long releaseTime) {
        this.releaseTime = releaseTime;
    }

    /**
     * Returns the release time.
     *
     * @return the release time
     */
    public long getReleaseTime() {
        return releaseTime;
    }

    /**
     * Returns an {@link URL} that points to the download page.
     *
     * @return the {@link URL} of the download page
     */
    public URL getDownloadUrl() {
        return downloadUrl;
    }

    /**
     * Returns the {@link URL} that points to the release note.
     *
     * @return the {@link URL} of the release note
     */
    public URL getReleaseNoteUrl() {
        return releaseNoteUrl;
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((downloadUrl == null) ? 0 : downloadUrl.hashCode());
        result = prime * result
                + ((releaseCode == null) ? 0 : releaseCode.hashCode());
        result = prime * result
                + ((releaseNoteUrl == null) ? 0 : releaseNoteUrl.hashCode());
        result = prime * result + (int) (releaseTime ^ (releaseTime >>> 32));
        return result;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ReleaseInformation other = (ReleaseInformation) obj;
        if (downloadUrl == null) {
            if (other.downloadUrl != null) {
                return false;
            }
        } else if (!downloadUrl.equals(other.downloadUrl)) {
            return false;
        }
        if (releaseCode == null) {
            if (other.releaseCode != null) {
                return false;
            }
        } else if (!releaseCode.equals(other.releaseCode)) {
            return false;
        }
        if (releaseNoteUrl == null) {
            if (other.releaseNoteUrl != null) {
                return false;
            }
        } else if (!releaseNoteUrl.equals(other.releaseNoteUrl)) {
            return false;
        }
        if (releaseTime != other.releaseTime) {
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(getClass().getSimpleName());
        sb.append('[').append("releaseNoteUrl:").append(releaseNoteUrl)
                .append(", releaseNoteDate:").append(releaseTime)
                .append(", downloadUrl:").append(downloadUrl).append(']');
        return sb.toString();
    }

    /**
     * Returns a {@link ReleaseInformation} from the json string.
     *
     * @param json
     *            a json string
     * @return a {@link ReleaseInformation} from the json string
     */
    public static ReleaseInformation fromJSON(final String json) {
        return new Gson().fromJson(json, ReleaseInformation.class);
    }
} public interface ReleaseInformationRepository {

    /**
     * Returns the ReleaseCode of the current installed version.
     *
     * @return the {@link ReleaseCode} of current installed version
     */
    ReleaseCode getInstalledReleaseCode();

    /**
     * Returns the {@link ReleaseInformation} of current installed version.
     *
     * @return the {@link ReleaseInformation} of current installed version
     */
    ReleaseInformation getInstalled();

    /**
     * Returns the {@link ReleaseInformation} of the latest release.
     *
     * @return the {@link ReleaseInformation} of the latest release
     */
    ReleaseInformation getLatest();

    /**
     * Returns the {@link ReleaseInformation} of the release identified by
     * {@link ReleaseCode}.
     *
     * @param releaseCode
     *            a {@link ReleaseCode}
     * @return a {@link ReleaseInformation} or null if any release was found
     */
    ReleaseInformation getByReleaseCode(ReleaseCode releaseCode);

} public class ReleaseInformationRepositoryImpl implements
        ReleaseInformationRepository {

    private static final int FETCH_TIMEOUT = 5000;

    /**
     * The pattern of the URL of the Elios update file.
     */
    public static final String URL_PATTERN = "{0}/{1}.euf";

    /**
     * The release info root URL preference key.
     */
    public static final String RELEASEINFO_ROOT_URL = "releaseinfo.root.url";

    /**
     * The current release preference key.
     */
    public static final String RELEASEINFO_CURENT_RELEASE_CODE = "releaseinfo.current.release";

    private final URI rootUrl;

    private final Preferences prefs;

    /**
     * Constructs a {@link ReleaseInformationRepositoryImpl} according to the
     * configuration stored in the {@link Preferences}.
     *
     * @param prefs
     *            a {@link Preferences} instance
     */
    public ReleaseInformationRepositoryImpl(final Preferences prefs) {
        String rootUrlStr = prefs.get(RELEASEINFO_ROOT_URL, null);
        if (rootUrlStr == null) {
            throw new IllegalStateException(
                    "Unable to retrieve the URL of the release information url");
        }
        rootUrl = URI.create(rootUrlStr);
        this.prefs = prefs;
    }

    /**
     * {@inheritDoc}
     *
     * @see net.eliosoft.elios.server.ReleaseInformationRepository#getInstalled()
     */
    @Override
    public ReleaseInformation getInstalled() {
        return getByReleaseCode(getInstalledReleaseCode());
    }

    /**
     * {@inheritDoc}
     *
     * @see net.eliosoft.elios.server.ReleaseInformationRepository#getLatest()
     */
    @Override
    public ReleaseInformation getLatest() {
        try {
            return checkout(buildLatestURI());
        } catch (MalformedURLException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * {@inheritDoc}
     *
     * @see net.eliosoft.elios.server.ReleaseInformationRepository#getByReleaseCode(net.eliosoft.elios.server.ReleaseCode)
     */
    @Override
    public ReleaseInformation getByReleaseCode(final ReleaseCode releaseCode) {
        try {
            return checkout(buildURI(releaseCode));
        } catch (MalformedURLException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
    }

    private ReleaseInformation checkout(final URI uri)
            throws MalformedURLException, IOException {
        InputStream stream = null;
        try {
            stream = streamOfUri(uri);

            StringBuilder sb = new StringBuilder();
            byte[] buf = new byte[4096];

            for (;;) {
                int i = stream.read(buf);
                if (i == -1) {
                    break;
                }
                sb.append(new String(buf, 0, i, Charset.forName("UTF-8")));
            }

            return ReleaseInformation.fromJSON(sb.toString());
        } finally {
            if (stream != null) {
                stream.close();
            }
        }
    }

    private InputStream streamOfUri(final URI uri) throws IOException,
            MalformedURLException {
        InputStream stream;
        final URLConnection cn = uri.toURL().openConnection();
        cn.setConnectTimeout(FETCH_TIMEOUT);
        stream = cn.getInputStream();
        return stream;
    }

    private URI buildURI(final ReleaseCode code) {
        return URI.create(MessageFormat.format(URL_PATTERN, rootUrl,
                code.getCode()));
    }

    private URI buildLatestURI() {
        return URI.create(MessageFormat.format(URL_PATTERN, rootUrl, "latest"));
    }

    /**
     * {@inheritDoc}
     *
     * @see net.eliosoft.elios.server.ReleaseInformationRepository#getInstalledReleaseCode()
     */
    @Override
    public ReleaseCode getInstalledReleaseCode() {
        return ReleaseCode.create(prefs.get(RELEASEINFO_CURENT_RELEASE_CODE,
                null));
    }
}
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.eliosoft.elios.gui.controllers.CuesController;
import net.eliosoft.elios.gui.controllers.DMXController;
import net.eliosoft.elios.gui.controllers.LogsController;
import net.eliosoft.elios.gui.controllers.PrefsController;
import net.eliosoft.elios.gui.controllers.RemoteController;
import net.eliosoft.elios.gui.models.DMXTableModel;
import net.eliosoft.elios.gui.models.LocaleComboBoxModel;
import net.eliosoft.elios.gui.models.RemoteModel;
import net.eliosoft.elios.gui.models.RemoteModel.BroadCastAddress;
import net.eliosoft.elios.gui.models.UpdateModel;
import net.eliosoft.elios.gui.views.AboutView;
import net.eliosoft.elios.gui.views.CuesView;
import net.eliosoft.elios.gui.views.DMXView;
import net.eliosoft.elios.gui.views.LogsLineView;
import net.eliosoft.elios.gui.views.LogsView;
import net.eliosoft.elios.gui.views.Messages;
import net.eliosoft.elios.gui.views.PrefsView;
import net.eliosoft.elios.gui.views.RemoteView;
import net.eliosoft.elios.gui.views.ViewInterface;
import net.eliosoft.elios.main.ApplicationState.State;
import net.eliosoft.elios.server.ArtNetServerManager;
import net.eliosoft.elios.server.CuesManager;
import net.eliosoft.elios.server.HttpServerManager;
import net.eliosoft.elios.server.ReleaseCode;
import net.eliosoft.elios.server.ReleaseInformation;
import net.eliosoft.elios.server.ReleaseInformationRepository;
import net.eliosoft.elios.server.ReleaseInformationRepositoryImpl;
import artnet4j.ArtNetException;
/*
 * This file is part of Elios.
 *
 * Copyright 2010 Jeremie GASTON-RAOUL & Alexandre COLLIGNON
 *
 * Elios is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Elios is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Elios. If not, see <http://www.gnu.org/licenses/>.
 */

package net.eliosoft.elios.main;




/**
 * Main Class of Elios Software contains the main method used to launch the
 * application.
 *
 * @author Jeremie GASTON-RAOUL
 */
public final class Elios {

    /**
     * The version that we set has installed.
     */
    private static final String ALREADY_INSTALLED_VERSION = "0.3-dev";

    /**
     * URL of the Eliosoft update site.
     */
    private static final String UPDATE_URL = "http://update.eliosoft.net/";

    /** Folder in which data are stored. **/
    private static final String ELIOS_DATA_FOLDER = System
            .getProperty("user.home") + File.separator + ".elios";

    /** Filename of the file used for cues list persistence. **/
    private static final String CUESLIST_FILENAME = ELIOS_DATA_FOLDER
            + File.separator + "elios.cues";

    /**
     * The logger.
     */
    private static final Logger LOGGER = LoggersManager.getInstance()
            .getLogger(Elios.class.getName());

    /**
     * "High res" icon.
     */
    private static final Image HIGH_RES_ICON_IMAGE = new ImageIcon(
            Elios.class
                    .getResource("/net/eliosoft/elios/gui/views/elios_e_64x64.png"))
            .getImage();

    /**
     * Icons array for the Elios application.
     */
    private static final Image[] ICONS = new Image[] {
            new ImageIcon(
                    Elios.class
                            .getResource("/net/eliosoft/elios/gui/views/elios_e_24x24.png"))
                    .getImage(),
            new ImageIcon(
                    Elios.class
                            .getResource("/net/eliosoft/elios/gui/views/elios_e_32x32.png"))
                    .getImage(),
            new ImageIcon(
                    Elios.class
                            .getResource("/net/eliosoft/elios/gui/views/elios_e_48x48.png"))
                    .getImage(), HIGH_RES_ICON_IMAGE };

    /**
     * Do nothing more than ensure that no object can be construct.
     */
    private Elios() {
        // nothing
    }

    /**
     * Launches Elios.
     *
     * @param args
     *            command-line argument. Currently unused !
     */
    public static void main(final String[] args) {

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e1) {
            LOGGER.info("Can not load system look and feel");
        }

        final Preferences prefs = Preferences.userNodeForPackage(Elios.class);

        Locale locale = loadLocale(prefs);

        try {
            final ApplicationState state = new ApplicationState();

            final RemoteModel remoteModel = createRemoteModel(prefs);

            final RemoteView remoteView = new RemoteView(remoteModel);

            UpdateModel uModel = new UpdateModel(prefs);
            // used to make relation between view and model
            new RemoteController(remoteModel, remoteView);

            final LocaleComboBoxModel localeModel = new LocaleComboBoxModel();
            localeModel.setSelectedItem(locale);
            PrefsView prefsView = new PrefsView(remoteModel, localeModel,
                    uModel);
            // used to make relation between view and model
            new PrefsController(remoteModel, localeModel, prefsView);