001package jmri.jmrix.ecos;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.ArrayList;
007import java.util.Enumeration;
008import java.util.Hashtable;
009import java.util.List;
010import java.util.ResourceBundle;
011import javax.annotation.Nonnull;
012import javax.swing.BorderFactory;
013import javax.swing.BoxLayout;
014import javax.swing.JButton;
015import javax.swing.JCheckBox;
016import javax.swing.JDialog;
017import javax.swing.JLabel;
018import javax.swing.JPanel;
019
020import jmri.*;
021import jmri.jmrix.ecos.utilities.GetEcosObjectNumber;
022import jmri.jmrix.ecos.utilities.RemoveObjectFromEcos;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026/**
027 * Implement turnout manager for Ecos systems.
028 * <p>
029 * System names are "UTnnn", where U is the user configurable system prefix,
030 * nnn is the turnout number without padding.
031 *
032 * @author Bob Jacobsen Copyright (C) 2001, 2008
033 */
034public class EcosTurnoutManager extends jmri.managers.AbstractTurnoutManager
035        implements EcosListener {
036
037    public EcosTurnoutManager(EcosSystemConnectionMemo memo) {
038        super(memo);
039        tc = getMemo().getTrafficController();
040
041        // listen for turnout creation
042        // connect to the TrafficManager
043        tc.addEcosListener(this);
044
045        // ask to be notified about newly created turnouts on the layout.
046        EcosMessage m = new EcosMessage("request(11, view)");
047        tc.sendEcosMessage(m, this);
048
049        // get initial state
050        m = new EcosMessage("queryObjects(11, addrext)");
051        tc.sendEcosMessage(m, this);
052        this.addPropertyChangeListener(this);
053    }
054
055    EcosTrafficController tc;
056
057    // The hash table simply holds the object number against the EcosTurnout ref.
058    private Hashtable<Integer, EcosTurnout> _tecos = new Hashtable<Integer, EcosTurnout>(); // stores known Ecos Object ids to DCC
059
060    /**
061     * {@inheritDoc}
062     */
063    @Override
064    @Nonnull
065    public EcosSystemConnectionMemo getMemo() {
066        return (EcosSystemConnectionMemo) memo;
067    }
068
069    @Override
070    public Turnout createNewTurnout(@Nonnull String systemName, String userName) {
071        int addr;
072        try {
073            addr = Integer.parseInt(systemName.substring(getSystemNamePrefix().length()));
074        } catch (java.lang.NumberFormatException e) {
075            throw new IllegalArgumentException("failed to convert systemName '"+systemName+"' to an Ecos turnout address");
076        }
077        Turnout t = new EcosTurnout(addr, getSystemPrefix(), tc, this);
078        t.setUserName(userName);
079        t.setFeedbackMode("MONITORING");
080        return t;
081    }
082
083    @Override
084    public boolean allowMultipleAdditions(@Nonnull String systemName) {
085        return true;
086    }
087
088    // to listen for status changes from Ecos system
089    @Override
090    public void reply(EcosReply m) {
091        log.debug("reply {}", m);
092        // is this a list of turnouts?
093        EcosTurnout et;
094
095        if (m.getResultCode() == 0) {
096            int ecosObjectId = m.getEcosObjectId();
097            if ((ecosObjectId != 11) && ((ecosObjectId < 20000) || (ecosObjectId > 30000))) {
098                log.debug("message received that is not within the valid turnout object range");
099                return;
100            }
101            List<String> headerDetails = m.getReplyHeaderDetails();
102            String[] msgContents = m.getContents();
103            //log.info("Initial Header " + headerDetails);
104            if (m.isUnsolicited()) {
105                if (ecosObjectId == 11) {
106                    //Creation or removal of a turnout from the Ecos.
107                    if (msgContents[0].contains("msg[LIST_CHANGED]")) {
108                        log.debug("We have received notification of a change in the Turnout list");
109                        EcosMessage mout = new EcosMessage("queryObjects(11)");
110                        tc.sendEcosMessage(mout, this);
111                    }
112                    //Creation or removal of a turnout from the Ecos.
113                } else {
114                    log.debug("Forwarding on State change for {}", ecosObjectId);
115                    et = _tecos.get(ecosObjectId);
116                    if (et != null) {
117                        et.reply(m);
118                        //As the event will come from one object, we shall check to see if it is an extended address,
119                        // if it is we also forward the message onto the slaved address.
120                        if (et.getExtended() != 0) {
121                            log.debug("This is also an extended turnout so forwarding on change to {}", et.getSlaveAddress());
122                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
123                            etx.reply(m);
124                        }
125                    }
126                }
127
128            } else {
129                String replyType = m.getReplyType();
130                if (replyType.equals("queryObjects")) {
131                    if (ecosObjectId == 11 && headerDetails.size() == 0) {
132                        //if (lines[0].startsWith("<REPLY queryObjects(11)>")) {
133                        log.debug("No sub details");
134                        checkTurnoutList(msgContents);
135                    } else if (headerDetails.contains("addr")) {
136                        // yes, make sure TOs exist
137                        //log.debug("found "+(lines.length-2)+" turnout objects");
138                        for (String item : m.getContents()) {
139                            log.debug("header {}", item);
140                            //for (int i = 1; i<lines.length-1; i++) {
141                            if (item.contains("addr")) { // skip odd lines
142                                int object = GetEcosObjectNumber.getEcosObjectNumber(item, null, " ");
143                                if ((20000 <= object) && (object < 30000)) { // only physical turnouts
144                                    int addr = GetEcosObjectNumber.getEcosObjectNumber(item, "[", "]");
145                                    log.debug("Found turnout object {} addr {}", object, addr);
146
147                                    if (addr > 0) {
148                                        Turnout t = getTurnout(getSystemNamePrefix() + addr);
149                                        if (t == null) {
150                                            et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr);
151                                            et.setObjectNumber(object);
152                                            _tecos.put(object, et);
153                                        }
154                                    }
155                                } else if ((30000 <= object) && (object < 40000)) {  //This is a ecos route
156                                    log.debug("Found route object {}", object);
157
158                                    Turnout t = getTurnout(getSystemNamePrefix() + object);
159                                    if (t == null) {
160                                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + object);
161                                        et.setObjectNumber(object);
162                                        _tecos.put(object, et);
163                                    }
164                                }
165                                if ((20000 <= object) && (object < 40000)) {
166                                    EcosMessage em = new EcosMessage("request(" + object + ",view)");
167                                    tc.sendEcosMessage(em, this);
168                                    em = new EcosMessage("get(" + object + ",state)");
169                                    tc.sendEcosMessage(em, this);
170                                }
171                            }
172                        }
173                    } else if (headerDetails.contains("addrext")) {
174                        //log.info("Extended");
175                        for (String item : m.getContents()) {
176                            //log.info(item);
177                            if (item.contains("addrext")) { // skip odd lines
178                                turnoutAddressDetails(item);
179                            }
180                        }
181                    }
182                } else if (replyType.equals("get")) {
183                    et = (EcosTurnout) getByEcosObject(ecosObjectId);
184                    if (headerDetails.contains("state")) {
185                        //As this is in response to a change in state we shall forward
186                        //it straight on to the ecos turnout to deal with.
187                        et.reply(m);
188                        //As the event will come from one object, we shall check to see if it is an extended address,
189                        // if it is we also forward the message onto the slaved address.
190                        if (et.getExtended() != 0) {
191                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
192                            etx.reply(m);
193                        }
194
195                    } else if (headerDetails.contains("symbol")) {
196                        //Extract symbol number and set on turnout.
197                        int symbol = GetEcosObjectNumber.getEcosObjectNumber(msgContents[0], "[", "]");
198                        et.setExtended(symbol);
199                        et.setTurnoutOperation(jmri.InstanceManager.getDefault(TurnoutOperationManager.class).getOperation("NoFeedback"));
200                        if ((symbol == 2) || (symbol == 4)) {
201
202                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
203                            etx.setExtended(symbol);
204                            etx.setTurnoutOperation(jmri.InstanceManager.getDefault(TurnoutOperationManager.class).getOperation("NoFeedback"));
205                            switch (symbol) {
206                                case 2:
207                                    et.setComment("Three Way Point with " + et.getSlaveAddress());
208                                    break;
209                                case 4:
210                                    et.setComment("Double Slip with " + et.getSlaveAddress());
211                                    break;
212                                default:
213                                    break;
214                            }
215                        }
216                        // get initial state
217                        EcosMessage em = new EcosMessage("get(" + ecosObjectId + ",state)");
218                        tc.sendEcosMessage(em, this);
219
220                    } else if (headerDetails.contains("addrext")) {
221                        turnoutAddressDetails(msgContents[0]);
222                    } else {
223                        String name = null;
224                        for (String li : msgContents) {
225                            if (li.contains("name")) {
226                                //start=li.indexOf("[")+2;
227                                //end=li.indexOf("]")-1;
228                                if ((name != null) /*&& (start!=end)*/) {
229                                    name = name + EcosReply.getContentDetail(li); /*" " + li.substring(start, end);*/
230
231                                } else {
232                                    name = EcosReply.getContentDetail(li); /*li.substring(start, end);*/
233
234                                }
235                            }
236                        }
237                        if (name != null) {
238                            et.setUserName(name);
239                        }
240                    }
241                } else if (ecosObjectId >= 20000) { // ecosObjectId <= 30000 is always true at this point (Spotbugs)
242                    log.debug("Reply for specific turnout");
243                    et = _tecos.get(ecosObjectId);
244                    if (et != null) {
245                        et.reply(m);
246                        //As the event will come from one object, we shall check to see if it is an extended address,
247                        // if it is we also forward the message onto the slaved address.
248                        if (et.getExtended() != 0) {
249                            log.debug("This is also an extended turnout so forwarding on change to {}", et.getSlaveAddress());
250                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
251                            etx.reply(m);
252                        }
253                    }
254                }
255            }
256        } else {
257            log.debug("Message received from Ecos is in error");
258        }
259    }
260
261    protected boolean addingTurnouts = false;
262
263    private void turnoutAddressDetails(String lines) {
264        addingTurnouts = true;
265        EcosTurnout et;
266        int start;
267        int end;
268        int object = GetEcosObjectNumber.getEcosObjectNumber(lines, null, " ");
269        if ((20000 <= object) && (object < 30000)) {
270            start = lines.indexOf('[') + 1;
271            end = lines.indexOf(']');
272            String turnoutadd = stripChar(lines.substring(start, end));
273            String[] straddr = turnoutadd.split(",");
274            log.debug("Number of Address for this device is {}", straddr.length);
275            if (straddr.length <= 2) {
276                if (straddr.length == 2) {
277                    if (!straddr[0].equals(straddr[1])) {
278                        log.debug("Addresses are not the same, we shall use the first address listed.");
279                    }
280                }
281                int addr = Integer.parseInt(straddr[0]);
282                if (addr > 0) {
283                    Turnout t = getTurnout(getSystemNamePrefix() + addr);
284                    if (t == null) {
285                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr);
286                        et.setObjectNumber(object);
287                        _tecos.put(object, et);
288                        // listen for changes
289                        EcosMessage em = new EcosMessage("request(" + object + ",view)");
290                        tc.sendEcosMessage(em, this);
291
292                        // get initial state
293                        em = new EcosMessage("get(" + object + ",state)");
294                        tc.sendEcosMessage(em, this);
295
296                        em = new EcosMessage("get(" + object + ", name1, name2, name3)");
297                        tc.sendEcosMessage(em, this);
298                    }
299                }
300
301            } else if (straddr.length == 4) {
302                log.debug("We have a two address object.");
303                //The first two addresses should be the same
304                if (!straddr[0].equals(straddr[1])) {
305                    log.debug("First Pair of Addresses are not the same, we shall use the first address");
306                }
307                if (!straddr[2].equals(straddr[3])) {
308                    log.debug("Second Pair of Addresses are not the same, we shall use the first address");
309                }
310                int addr = Integer.parseInt(straddr[0]);
311                int addr2 = Integer.parseInt(straddr[2]);
312                if (addr > 0) {
313                    //addr = straddr[0];
314                    Turnout t = getTurnout(getSystemNamePrefix() + addr);
315                    if (t == null) {
316                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr);
317                        et.setObjectNumber(object);
318                        et.setSlaveAddress(addr2);
319                        _tecos.put(object, et);
320
321                        //Get the type of accessory...
322                        EcosMessage em = new EcosMessage("get(" + object + ",symbol)");
323                        tc.sendEcosMessage(em, this);
324
325                        // listen for changes
326                        em = new EcosMessage("request(" + object + ",view)");
327                        tc.sendEcosMessage(em, this);
328
329                        em = new EcosMessage("get(" + object + ", name1, name2, name3)");
330                        tc.sendEcosMessage(em, this);
331                    }
332                }
333
334                if (addr2 > 0) {
335                    Turnout t = getTurnout(getSystemNamePrefix() + addr2);
336                    if (t == null) {
337                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr2);
338                        et.setMasterObjectNumber(false);
339                        et.setObjectNumber(object);
340                        et.setComment("Extended address linked with turnout " + getSystemPrefix() + "T" + straddr[0]);
341                    }
342                }
343            }
344
345        } else if ((30000 <= object) && (object < 40000)) {  //This is a ecos route
346
347            log.debug("Found route object {}", object);
348
349            Turnout t = getTurnout(getSystemNamePrefix() + object);
350            if (t == null) {
351                et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + object);
352                et.setObjectNumber(object);
353                _tecos.put(object, et);
354
355                // get initial state
356                EcosMessage em = new EcosMessage("get(" + object + ",state)");
357                tc.sendEcosMessage(em, this);
358                //Need to do some more work on routes on the ecos.
359
360                // listen for changes
361                // em = new EcosMessage("request("+object+",view)");
362                // tc.sendEcosMessage(em, null);
363                // get the name from the ecos to set as Username
364                em = new EcosMessage("get(" + object + ", name1, name2, name3)");
365                tc.sendEcosMessage(em, this);
366            }
367        }
368        addingTurnouts = false;
369    }
370
371    /* This is used after an event update form the ecos informing us of a change in the 
372     * turnout list, we have to determine if it is an addition or delete.
373     * We should only ever do either a remove or an add in one go.
374     */
375    void checkTurnoutList(String[] ecoslines) {
376        final EcosPreferences p = getMemo().getPreferenceManager();
377
378        String[] jmrilist = getEcosObjectArray();
379        boolean nomatch = true;
380        int intTurnout = 0;
381        String strECOSTurnout = null;
382        for (int i = 0; i < jmrilist.length; i++) {
383            nomatch = true;
384            String strJMRITurnout = jmrilist[i];
385            intTurnout = Integer.parseInt(strJMRITurnout);
386            for (String li : ecoslines) {
387                strECOSTurnout = li.replaceAll("[\\n\\r]", "");
388                if (strECOSTurnout.equals(strJMRITurnout)) {
389                    nomatch = false;
390                    break;
391                }
392            }
393
394            if (nomatch) {
395                final EcosTurnout et = (EcosTurnout) getByEcosObject(intTurnout);
396                _tecos.remove(intTurnout);
397                if (p.getRemoveTurnoutsFromJMRI() == 0x02) {
398                    //Remove turnout
399                    _tecos.remove(et.getObject());
400                    deregister(et);
401                } else if (p.getRemoveTurnoutsFromJMRI() == 0x00) {
402                    final JDialog dialog = new JDialog();
403                    dialog.setTitle(Bundle.getMessage("DeleteTurnoutTitle"));
404                    dialog.setLocationRelativeTo(null);
405                    dialog.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
406                    JPanel container = new JPanel();
407                    container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
408                    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
409
410                    JLabel question = new JLabel(Bundle.getMessage("RemoveTurnoutLine1", et.getDisplayName()));
411                    question.setAlignmentX(Component.CENTER_ALIGNMENT);
412                    container.add(question);
413                    question = new JLabel(Bundle.getMessage("RemoveTurnoutLine2"));
414                    question.setAlignmentX(Component.CENTER_ALIGNMENT);
415                    container.add(question);
416                    final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting"));
417                    remember.setFont(remember.getFont().deriveFont(10f));
418                    remember.setAlignmentX(Component.CENTER_ALIGNMENT);
419
420                    JButton yesButton = new JButton(Bundle.getMessage("ButtonYes"));
421                    JButton noButton = new JButton(Bundle.getMessage("ButtonNo"));
422                    JPanel button = new JPanel();
423                    button.setAlignmentX(Component.CENTER_ALIGNMENT);
424                    button.add(yesButton);
425                    button.add(noButton);
426                    container.add(button);
427
428                    noButton.addActionListener(new ActionListener() {
429                        @Override
430                        public void actionPerformed(ActionEvent e) {
431                            if (remember.isSelected()) {
432                                p.setRemoveTurnoutsFromJMRI(0x01);
433                            }
434                            dialog.dispose();
435                        }
436                    });
437
438                    yesButton.addActionListener(new ActionListener() {
439                        final ResourceBundle rb = ResourceBundle.getBundle("jmri.jmrit.beantable.BeanTableBundle");
440
441                        @Override
442                        public void actionPerformed(ActionEvent e) {
443                            if (remember.isSelected()) {
444                                p.setRemoveTurnoutsFromJMRI(0x02);
445                            }
446                            int count = et.getNumPropertyChangeListeners() - 1; // one is this table
447                            if (log.isDebugEnabled()) {
448                                log.debug("Delete with {}", count);
449                            }
450                            if ((!noWarnDelete) && (count > 0)) {
451                                String msg = java.text.MessageFormat.format(
452                                        rb.getString("DeletePrompt") + "\n"
453                                        + rb.getString("ReminderInUse"),
454                                        new Object[]{et.getSystemName(), "" + count});
455                                // verify deletion
456                                int val = javax.swing.JOptionPane.showOptionDialog(null,
457                                        msg, Bundle.getMessage("WarningTitle"),
458                                        javax.swing.JOptionPane.YES_NO_CANCEL_OPTION, javax.swing.JOptionPane.QUESTION_MESSAGE, null,
459                                        new Object[]{Bundle.getMessage("ButtonYes"),
460                                            rb.getString("ButtonYesPlus"),
461                                                Bundle.getMessage("ButtonNo")},
462                                        Bundle.getMessage("ButtonNo"));
463                                if (val == 2) {
464                                    _tecos.remove(et.getObject());
465                                    deregister(et);
466                                    dialog.dispose();
467                                    return;  // return without deleting
468                                }
469                                if (val == 1) { // suppress future warnings
470                                    noWarnDelete = true;
471                                }
472                            }
473                            // finally OK, do the actual delete
474                            deleteEcosTurnout(et);
475                            dialog.dispose();
476                        }
477                    });
478                    container.add(remember);
479                    container.setAlignmentX(Component.CENTER_ALIGNMENT);
480                    container.setAlignmentY(Component.CENTER_ALIGNMENT);
481                    dialog.getContentPane().add(container);
482                    dialog.pack();
483                    dialog.setModal(true);
484                    dialog.setVisible(true);
485                } else {
486                    //We will need to remove the turnout from our list as it no longer exists on the ecos.
487                    _tecos.remove(et.getObject());
488                }
489            }
490        }
491        int turnout;
492        for (String li : ecoslines) {
493            String tmpturn = li.replaceAll("[\\n\\r]", "");
494            turnout = Integer.parseInt(tmpturn);
495            if (getByEcosObject(turnout) == null) {
496                EcosMessage mout = new EcosMessage("get(" + turnout + ", addrext)");
497                tc.sendEcosMessage(mout, this);
498            }
499        }
500    }
501
502    private boolean noWarnDelete = false;
503
504    public String stripChar(String s) {
505        String allowed
506                = ",0123456789";
507        StringBuilder result = new StringBuilder();
508        for (int i = 0; i < s.length(); i++) {
509            if (allowed.indexOf(s.charAt(i)) >= 0) {
510                result.append(s.charAt(i));
511            }
512        }
513
514        return result.toString();
515    }
516
517    @Override
518    public void message(EcosMessage m) {
519        // messages are ignored
520    }
521
522    @Override
523    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "UCF_USELESS_CONTROL_FLOW", 
524        justification = "OK to compare floats, as even tiny differences should trigger update")
525    public void propertyChange(java.beans.PropertyChangeEvent e) {
526        if ((e.getPropertyName().equals("length")) && (!addingTurnouts)) {
527            final EcosPreferences p = getMemo().getPreferenceManager();
528            EcosTurnout et;
529            String[] ecoslist = this.getEcosObjectArray();
530            
531             for (Turnout turnout : getNamedBeanSet()) {
532                if (turnout.getSystemName().startsWith(getSystemNamePrefix())) {
533                    et = (EcosTurnout) turnout;
534                    if (et.getObject() == 0) {
535                        //We do not support this yet at there are many parameters
536                        // when creating a turnout on the ecos.
537                    }
538                }
539            }
540
541            for (int i = 0; i < ecoslist.length; i++) {
542                et = (EcosTurnout) getByEcosObject(Integer.parseInt(ecoslist[i]));
543                int address = et.getNumber();
544                if (getBySystemName(getSystemNamePrefix() + address) == null) {
545                    if (p.getRemoveTurnoutsFromEcos() == 0x02) {
546                        RemoveObjectFromEcos removeObjectFromEcos = new RemoveObjectFromEcos();
547                        removeObjectFromEcos.removeObjectFromEcos("" + et.getObject(), tc);
548                        deleteEcosTurnout(et);
549                    } else {
550                        final EcosTurnout etd = et;
551                        final JDialog dialog = new JDialog();
552                        dialog.setTitle(Bundle.getMessage("RemoveTurnoutTitle"));
553                        dialog.setLocation(300, 200);
554                        dialog.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
555                        JPanel container = new JPanel();
556                        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
557                        container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
558
559                        JLabel question = new JLabel(Bundle.getMessage("RemoveTurnoutX", etd.getSystemName()));
560                        question.setAlignmentX(Component.CENTER_ALIGNMENT);
561                        container.add(question);
562                        final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting"));
563                        remember.setFont(remember.getFont().deriveFont(10f));
564                        remember.setAlignmentX(Component.CENTER_ALIGNMENT);
565                        remember.setVisible(true);
566                        JButton yesButton = new JButton(Bundle.getMessage("ButtonYes"));
567                        JButton noButton = new JButton(Bundle.getMessage("ButtonNo"));
568                        JPanel button = new JPanel();
569                        button.setAlignmentX(Component.CENTER_ALIGNMENT);
570                        button.add(yesButton);
571                        button.add(noButton);
572                        container.add(button);
573
574                        noButton.addActionListener(new ActionListener() {
575                            @Override
576                            public void actionPerformed(ActionEvent e) {
577                                if (remember.isSelected()) {
578                                    p.setRemoveTurnoutsFromEcos(0x01);
579                                }
580                                dialog.dispose();
581                            }
582                        });
583
584                        yesButton.addActionListener(new ActionListener() {
585                            @Override
586                            public void actionPerformed(ActionEvent e) {
587                                if (remember.isSelected()) {
588                                    p.setRemoveTurnoutsFromEcos(0x02);
589                                }
590                                RemoveObjectFromEcos removeObjectFromEcos = new RemoveObjectFromEcos();
591                                removeObjectFromEcos.removeObjectFromEcos("" + etd.getObject(), tc);
592                                deleteEcosTurnout(etd);
593                                dialog.dispose();
594                            }
595                        });
596                        container.add(remember);
597                        container.setAlignmentX(Component.CENTER_ALIGNMENT);
598                        container.setAlignmentY(Component.CENTER_ALIGNMENT);
599                        dialog.getContentPane().add(container);
600                        dialog.pack();
601                        dialog.setModal(true);
602                        dialog.setVisible(true);
603                    }
604                }
605            }
606        }
607        super.propertyChange(e);
608    }
609
610    public void deleteEcosTurnout(EcosTurnout et) {
611        addingTurnouts = true;
612        deregister(et);
613        et.dispose();
614        EcosMessage em = new EcosMessage("release(" + et.getObject() + ",view)");
615        tc.sendEcosMessage(em, this);
616        _tecos.remove(Integer.valueOf(et.getObject()));
617        addingTurnouts = false;
618    }
619
620    @Override
621    public void dispose() {
622        Enumeration<Integer> en = _tecos.keys();
623        EcosMessage em;
624        while (en.hasMoreElements()) {
625            int ecosObject = en.nextElement();
626            em = new EcosMessage("release(" + ecosObject + ",view)");
627            tc.sendEcosMessage(em, this);
628        }
629
630        if (jmri.InstanceManager.getNullableDefault(ConfigureManager.class) != null) {
631            jmri.InstanceManager.getDefault(ConfigureManager.class).deregister(this);
632        }
633        _tecos.clear();
634    }
635
636    public List<String> getEcosObjectList() {
637        String[] arr = new String[_tecos.size()];
638        List<String> out = new ArrayList<String>();
639        Enumeration<Integer> en = _tecos.keys();
640        int i = 0;
641        while (en.hasMoreElements()) {
642            arr[i] = "" + en.nextElement();
643            i++;
644        }
645        java.util.Arrays.sort(arr);
646        for (i = 0; i < arr.length; i++) {
647            out.add(arr[i]);
648        }
649        return out;
650    }
651
652    public String[] getEcosObjectArray() {
653        String[] arr = new String[_tecos.size()];
654        Enumeration<Integer> en = _tecos.keys();
655        int i = 0;
656        while (en.hasMoreElements()) {
657            arr[i] = "" + en.nextElement();
658            i++;
659        }
660        java.util.Arrays.sort(arr);
661        return arr;
662    }
663
664    public Turnout getByEcosObject(int ecosObject) {
665        return _tecos.get(Integer.valueOf(ecosObject));
666    }
667
668    public void refreshItems() {
669        /*ask to be notified about newly created turnouts on the layout.
670         Doing the request to view the list, will also kick off a request to 
671         view on each individual turnout*/
672        EcosMessage m = new EcosMessage("request(11, view)");
673        tc.sendEcosMessage(m, this);
674        for (Integer ecosObjectId : _tecos.keySet()) {
675            EcosMessage em = new EcosMessage("release(" + ecosObjectId + ",view)");
676            tc.sendEcosMessage(em, this);
677            em = new EcosMessage("get(" + ecosObjectId + ",state)");
678            tc.sendEcosMessage(em, this);
679            em = new EcosMessage("request(" + ecosObjectId + ",view)");
680            tc.sendEcosMessage(em, this);
681        }
682    }
683    
684    @Override
685    @Nonnull
686    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
687        int iName;
688        try {
689                iName = Integer.parseInt(curAddress);
690            } catch (NumberFormatException ex) {
691                throw new JmriException("Hardware Address passed "+curAddress+" should be a number.");
692            }
693        return prefix + typeLetter() + iName;
694    }
695    
696    /**
697     * Validates to contain at least 1 number . . .
698     * <p>
699     * TODO: Custom validation for EcosTurnoutManager could be improved.
700     * {@inheritDoc}
701     */
702    @Override
703    @Nonnull
704    public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException {
705        return validateSystemNameFormatOnlyNumeric(name, locale);
706    }
707
708    private final static Logger log = LoggerFactory.getLogger(EcosTurnoutManager.class);
709
710}