001package jmri.jmrit.operations.trains.manualtrainbuilder.gui;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.swing.*;
009import javax.swing.table.TableCellEditor;
010
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014import jmri.InstanceManager;
015import jmri.jmrit.operations.OperationsTableModel;
016import jmri.jmrit.operations.locations.*;
017import jmri.jmrit.operations.rollingstock.cars.*;
018import jmri.jmrit.operations.routes.Route;
019import jmri.jmrit.operations.routes.RouteLocation;
020import jmri.jmrit.operations.setup.Control;
021import jmri.jmrit.operations.trains.Train;
022import jmri.jmrit.operations.trains.TrainManager;
023import jmri.jmrit.operations.trains.manualtrainbuilder.TrainManualBuild;
024import jmri.jmrit.operations.trains.manualtrainbuilder.TrainManualBuildItem;
025import jmri.jmrit.operations.trains.schedules.TrainSchedule;
026import jmri.jmrit.operations.trains.schedules.TrainScheduleManager;
027import jmri.util.swing.XTableColumnModel;
028import jmri.util.table.ButtonEditor;
029import jmri.util.table.ButtonRenderer;
030
031/**
032 * Table Model for edit of a schedule used by operations
033 *
034 * @author Daniel Boudreau Copyright (C) 2009, 2014
035 */
036public class TrainManualBuildTableModel extends OperationsTableModel implements PropertyChangeListener {
037
038    // Defines the columns
039    private static final int ID_COLUMN = 0;
040    private static final int TYPE_COLUMN = ID_COLUMN + 1;
041    private static final int ROAD_COLUMN = TYPE_COLUMN + 1;
042    private static final int LOAD_COLUMN = ROAD_COLUMN + 1;
043    private static final int LOCATION_COLUMN = LOAD_COLUMN + 1;
044    private static final int LOC_TRACK_COLUMN = LOCATION_COLUMN + 1;
045    private static final int DESTINATION_COLUMN = LOC_TRACK_COLUMN + 1;
046    private static final int DEST_TRACK_COLUMN = DESTINATION_COLUMN + 1;
047    private static final int PICKUP_DAY_COLUMN = DEST_TRACK_COLUMN + 1;
048    private static final int COUNT_COLUMN = PICKUP_DAY_COLUMN + 1;
049    private static final int WARN_COLUMN = COUNT_COLUMN + 1;
050    private static final int FAIL_COLUMN = WARN_COLUMN + 1;
051    private static final int REMOVE_COLUMN = FAIL_COLUMN + 1;
052    private static final int UP_COLUMN = REMOVE_COLUMN + 1;
053    private static final int DOWN_COLUMN = UP_COLUMN + 1;
054    private static final int DELETE_COLUMN = DOWN_COLUMN + 1;
055
056    private static final int HIGHEST_COLUMN = DELETE_COLUMN + 1;
057
058    private static final String NONE = "";
059
060    public TrainManualBuildTableModel() {
061        super();
062    }
063
064    TrainManualBuild _manualBuild;
065    TrainManualBuildEditFrame _frame;
066    Train _train;
067
068    private void updateList() {
069        // first, remove listeners from the individual objects
070        removePropertyChangeManualBuildItems();
071        _list = _manualBuild.getItemsBySequenceList();
072        // and add them back in
073        for (TrainManualBuildItem mbi : _list) {
074            mbi.addPropertyChangeListener(this);
075            // covers the cases where destination or track is deleted
076            if (mbi.getDestination() != null) {
077                mbi.getDestination().addPropertyChangeListener(this);
078            }
079            if (mbi.getDestinationTrack() != null) {
080                mbi.getDestinationTrack().addPropertyChangeListener(this);
081            }
082        }
083    }
084
085    List<TrainManualBuildItem> _list = new ArrayList<>();
086
087    protected void initTable(TrainManualBuildEditFrame frame, JTable table, TrainManualBuild tmb) {
088        super.initTable(table);
089        _manualBuild = tmb;
090        _frame = frame;
091        _train = InstanceManager.getDefault(TrainManager.class).getTrainById(_manualBuild.getTrainId());
092
093        // add property listeners
094        _manualBuild.addPropertyChangeListener(this);
095        initTable();
096    }
097
098    private void initTable() {
099        // Use XTableColumnModel so we can control which columns are visible
100        XTableColumnModel tcm = new XTableColumnModel();
101        _table.setColumnModel(tcm);
102        _table.createDefaultColumnsFromModel();
103
104        // Install the button handlers
105        ButtonRenderer buttonRenderer = new ButtonRenderer();
106        TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton());
107        tcm.getColumn(UP_COLUMN).setCellRenderer(buttonRenderer);
108        tcm.getColumn(UP_COLUMN).setCellEditor(buttonEditor);
109        tcm.getColumn(DOWN_COLUMN).setCellRenderer(buttonRenderer);
110        tcm.getColumn(DOWN_COLUMN).setCellEditor(buttonEditor);
111        tcm.getColumn(DELETE_COLUMN).setCellRenderer(buttonRenderer);
112        tcm.getColumn(DELETE_COLUMN).setCellEditor(buttonEditor);
113
114        // set column preferred widths
115        _table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(35);
116        _table.getColumnModel().getColumn(TYPE_COLUMN).setPreferredWidth(90);
117        _table.getColumnModel().getColumn(ROAD_COLUMN).setPreferredWidth(90);
118        _table.getColumnModel().getColumn(LOAD_COLUMN).setPreferredWidth(90);
119        _table.getColumnModel().getColumn(LOCATION_COLUMN).setPreferredWidth(130);
120        _table.getColumnModel().getColumn(LOC_TRACK_COLUMN).setPreferredWidth(130);
121        _table.getColumnModel().getColumn(DESTINATION_COLUMN).setPreferredWidth(130);
122        _table.getColumnModel().getColumn(DEST_TRACK_COLUMN).setPreferredWidth(130);
123        _table.getColumnModel().getColumn(PICKUP_DAY_COLUMN).setPreferredWidth(90);
124        _table.getColumnModel().getColumn(COUNT_COLUMN).setPreferredWidth(45);
125        _table.getColumnModel().getColumn(WARN_COLUMN).setPreferredWidth(45);
126        _table.getColumnModel().getColumn(FAIL_COLUMN).setPreferredWidth(45);
127        _table.getColumnModel().getColumn(REMOVE_COLUMN).setPreferredWidth(60);
128        _table.getColumnModel().getColumn(UP_COLUMN).setPreferredWidth(60);
129        _table.getColumnModel().getColumn(DOWN_COLUMN).setPreferredWidth(70);
130        _table.getColumnModel().getColumn(DELETE_COLUMN).setPreferredWidth(70);
131
132        _frame.loadTableDetails(_table);
133
134        // does not use a table sorter
135        _table.setRowSorter(null);
136
137        updateList();
138    }
139
140    @Override
141    public int getRowCount() {
142        return _list.size();
143    }
144
145    @Override
146    public int getColumnCount() {
147        return HIGHEST_COLUMN;
148    }
149
150    @Override
151    public String getColumnName(int col) {
152        switch (col) {
153            case ID_COLUMN:
154                return Bundle.getMessage("Id");
155            case TYPE_COLUMN:
156                return Bundle.getMessage("Type");
157            case ROAD_COLUMN:
158                return Bundle.getMessage("Road");
159            case LOAD_COLUMN:
160                return Bundle.getMessage("Load");
161            case LOCATION_COLUMN:
162                return Bundle.getMessage("Location");
163            case LOC_TRACK_COLUMN:
164                return Bundle.getMessage("Track");
165            case DESTINATION_COLUMN:
166                return Bundle.getMessage("Destination");
167            case DEST_TRACK_COLUMN:
168                return Bundle.getMessage("DestTrack");
169            case PICKUP_DAY_COLUMN:
170                return Bundle.getMessage("Pickup");
171            case COUNT_COLUMN:
172                return Bundle.getMessage("Count");
173            case WARN_COLUMN:
174                return Bundle.getMessage("Warn");
175            case FAIL_COLUMN:
176                return Bundle.getMessage("Fail");
177            case REMOVE_COLUMN:
178                return Bundle.getMessage("Remove");
179            case UP_COLUMN:
180                return Bundle.getMessage("Up");
181            case DOWN_COLUMN:
182                return Bundle.getMessage("Down");
183            case DELETE_COLUMN:
184                return Bundle.getMessage("ButtonDelete");
185            default:
186                return "unknown"; // NOI18N
187        }
188    }
189
190    @Override
191    public Class<?> getColumnClass(int col) {
192        switch (col) {
193            case ID_COLUMN:
194                return String.class;
195            case TYPE_COLUMN:
196            case ROAD_COLUMN:
197            case LOAD_COLUMN:
198            case LOCATION_COLUMN:
199            case LOC_TRACK_COLUMN:
200            case DESTINATION_COLUMN:
201            case DEST_TRACK_COLUMN:
202            case PICKUP_DAY_COLUMN:
203                return JComboBox.class;
204            case COUNT_COLUMN:
205                return Integer.class;
206            case WARN_COLUMN:
207            case FAIL_COLUMN:
208            case REMOVE_COLUMN:
209                return Boolean.class;
210            case UP_COLUMN:
211            case DOWN_COLUMN:
212            case DELETE_COLUMN:
213                return JButton.class;
214            default:
215                return null;
216        }
217    }
218
219    @Override
220    public boolean isCellEditable(int row, int col) {
221        switch (col) {
222            case TYPE_COLUMN:
223            case ROAD_COLUMN:
224            case LOAD_COLUMN:
225            case LOCATION_COLUMN:
226            case LOC_TRACK_COLUMN:
227            case DESTINATION_COLUMN:
228            case DEST_TRACK_COLUMN:
229            case PICKUP_DAY_COLUMN:
230            case COUNT_COLUMN:
231            case WARN_COLUMN:
232            case FAIL_COLUMN:
233            case REMOVE_COLUMN:
234            case UP_COLUMN:
235            case DOWN_COLUMN:
236            case DELETE_COLUMN:
237                return true;
238            default:
239                return false;
240        }
241    }
242
243    @Override
244    public Object getValueAt(int row, int col) {
245        if (row >= getRowCount()) {
246            return "ERROR row " + row; // NOI18N
247        }
248        TrainManualBuildItem mbi = _list.get(row);
249        if (mbi == null) {
250            return "ERROR schedule item unknown " + row; // NOI18N
251        }
252        switch (col) {
253            case ID_COLUMN:
254                return mbi.getId();
255            case TYPE_COLUMN:
256                return getTypeComboBox(mbi);
257            case ROAD_COLUMN:
258                return getRoadComboBox(mbi);
259            case LOAD_COLUMN:
260                return getLoadComboBox(mbi);
261            case LOCATION_COLUMN:
262                return getRouteLocationComboBox(mbi);
263            case LOC_TRACK_COLUMN:
264                return getLocationTrackComboBox(mbi);
265            case DESTINATION_COLUMN:
266                return getDestinationComboBox(mbi);
267            case DEST_TRACK_COLUMN:
268                return getDestTrackComboBox(mbi);
269            case PICKUP_DAY_COLUMN:
270                return getPickupDayComboBox(mbi);
271            case COUNT_COLUMN:
272                return mbi.getCount();
273            case WARN_COLUMN:
274                return mbi.isWarnEnabled();
275            case FAIL_COLUMN:
276                return mbi.isFailEnabled();
277            case REMOVE_COLUMN:
278                return mbi.isRemoveEnabled();
279            case UP_COLUMN:
280                return Bundle.getMessage("Up");
281            case DOWN_COLUMN:
282                return Bundle.getMessage("Down");
283            case DELETE_COLUMN:
284                return Bundle.getMessage("ButtonDelete");
285            default:
286                return "unknown " + col; // NOI18N
287        }
288    }
289
290    @Override
291    public void setValueAt(Object value, int row, int col) {
292        if (value == null) {
293            log.debug("Warning schedule table row {} still in edit", row);
294            return;
295        }
296        TrainManualBuildItem mbi = _list.get(row);
297        switch (col) {
298            case TYPE_COLUMN:
299                setType(value, mbi);
300                break;
301            case ROAD_COLUMN:
302                setRoad(value, mbi);
303                break;
304            case LOAD_COLUMN:
305                setLoad(value, mbi);
306                break;
307            case LOCATION_COLUMN:
308                setRouteLocation(value, mbi);
309                break;
310            case LOC_TRACK_COLUMN:
311                setLocTrack(value, mbi);
312                break;
313            case DESTINATION_COLUMN:
314                setDestination(value, mbi);
315                break;
316            case DEST_TRACK_COLUMN:
317                setDestTrack(value, mbi);
318                break;
319            case PICKUP_DAY_COLUMN:
320                setPickupDay(value, mbi);
321                break;
322            case COUNT_COLUMN:
323                setCount(value, mbi);
324                break;
325            case WARN_COLUMN:
326                setWarn(value, mbi);
327                break;
328            case FAIL_COLUMN:
329                setFail(value, mbi);
330                break;
331            case REMOVE_COLUMN:
332                mbi.setRemoveEnabled(((Boolean) value).booleanValue());
333                break;
334            case UP_COLUMN:
335                moveUpManualBuildItem(row);
336                break;
337            case DOWN_COLUMN:
338                moveDownManualBuildItem(row);
339                break;
340            case DELETE_COLUMN:
341                deleteManualBuildItem(row);
342                break;
343            default:
344                break;
345        }
346    }
347
348    private JComboBox<String> getTypeComboBox(TrainManualBuildItem mbi) {
349        JComboBox<String> cb = new JComboBox<>();
350        cb.addItem(NONE);
351        for (String typeName : InstanceManager.getDefault(CarTypes.class).getNames()) {
352            if (_train.isTypeNameAccepted(typeName)) {
353                cb.addItem(typeName);
354            }
355        }
356        cb.setSelectedItem(mbi.getTypeName());
357        // fix if type no longer accepted by train
358        if (!cb.getSelectedItem().equals(mbi.getTypeName())) {
359            mbi.setTypeName(NONE);
360        }
361        return cb;
362    }
363
364    private JComboBox<String> getRoadComboBox(TrainManualBuildItem mbi) {
365        JComboBox<String> cb = new JComboBox<>();
366        cb.addItem(NONE);
367        for (String roadName : InstanceManager.getDefault(CarRoads.class).getNames(mbi.getTypeName())) {
368            if (_train.isCarRoadNameAccepted(roadName)) {
369                cb.addItem(roadName);
370            }
371        }
372        cb.setSelectedItem(mbi.getRoadName());
373        // fix if road no longer accepted by train
374        if (!cb.getSelectedItem().equals(mbi.getRoadName())) {
375            mbi.setRoadName(NONE);
376        }
377        return cb;
378    }
379
380    protected JComboBox<String> getLoadComboBox(TrainManualBuildItem mbi) {
381        JComboBox<String> cb = InstanceManager.getDefault(CarLoads.class).getSelectComboBox(mbi.getTypeName());
382        for (String loadName : InstanceManager.getDefault(CarLoads.class).getNames(mbi.getTypeName())) {
383            if (!_train.isLoadNameAccepted(loadName)) {
384                cb.removeItem(loadName);
385            }
386        }
387        cb.setSelectedItem(mbi.getLoadName());
388        // fix if load no longer accepted by train
389        if (!cb.getSelectedItem().equals(mbi.getLoadName())) {
390            mbi.setLoadName(NONE);
391        }
392        return cb;
393    }
394
395    protected JComboBox<RouteLocation> getRouteLocationComboBox(TrainManualBuildItem mbi) {
396        JComboBox<RouteLocation> cb = new JComboBox<>();
397        cb.addItem(null);
398        Route route = _train.getRoute();
399        for (RouteLocation rl : route.getLocationsBySequenceList()) {
400            cb.addItem(rl);
401        }
402        filterRouteLocations(cb, mbi.getTypeName());
403        cb.setSelectedItem(mbi.getRouteLocation());
404        // fix if route location no longer exists
405        if (cb.getSelectedItem() != mbi.getRouteLocation()) {
406            mbi.setRouteLocation(null);
407        }
408        return cb;
409    }
410
411    protected JComboBox<Track> getLocationTrackComboBox(TrainManualBuildItem mbi) {
412        // log.debug("getTrackComboBox for ManualBuildItem "+si.getType());
413        JComboBox<Track> cb = new JComboBox<>();
414        if (mbi.getRouteLocation() != null) {
415            Location loc = mbi.getRouteLocation().getLocation();
416            loc.updateComboBox(cb);
417            filterTracks(loc, cb, mbi.getTypeName(), mbi.getRoadName(), mbi.getLoadName());
418        }
419        cb.setSelectedItem(mbi.getLocationTrack());
420        // fix if track no longer exists
421        if (cb.getSelectedItem() != mbi.getLocationTrack()) {
422            mbi.setLocationTrack(null);
423        }
424        return cb;
425    }
426
427    protected JComboBox<Location> getDestinationComboBox(TrainManualBuildItem mbi) {
428        JComboBox<Location> cb = InstanceManager.getDefault(LocationManager.class).getComboBox();
429        filterLocations(cb, mbi.getTypeName());
430        cb.setSelectedItem(mbi.getDestination());
431        // fix if destination no longer exists
432        if (cb.getSelectedItem() != mbi.getDestination()) {
433            mbi.setDestination(null);
434        }
435        return cb;
436    }
437
438    protected JComboBox<Track> getDestTrackComboBox(TrainManualBuildItem mbi) {
439        // log.debug("getTrackComboBox for ManualBuildItem "+si.getType());
440        JComboBox<Track> cb = new JComboBox<>();
441        if (mbi.getDestination() != null) {
442            Location dest = mbi.getDestination();
443            dest.updateComboBox(cb);
444            filterTracks(dest, cb, mbi.getTypeName(), mbi.getRoadName(), mbi.getLoadName());
445        }
446        cb.setSelectedItem(mbi.getDestinationTrack());
447        // fix if track no longer exists
448        if (cb.getSelectedItem() != mbi.getDestinationTrack()) {
449            mbi.setDestinationTrack(null);
450        }
451        return cb;
452    }
453
454    private JComboBox<TrainSchedule> getPickupDayComboBox(TrainManualBuildItem mbi) {
455        JComboBox<TrainSchedule> cb = InstanceManager.getDefault(TrainScheduleManager.class).getSelectComboBox();
456        TrainSchedule sch =
457                InstanceManager.getDefault(TrainScheduleManager.class).getScheduleById(mbi.getTrainScheduleId());
458        cb.setSelectedItem(sch);
459        // fix if schedule no longer exists
460        if (cb.getSelectedItem() != sch) {
461            mbi.setTrainScheduleId(NONE);
462        }
463        return cb;
464    }
465
466    private void setType(Object value, TrainManualBuildItem mbi) {
467        String type = (String) ((JComboBox<?>) value).getSelectedItem();
468        mbi.setTypeName(type);
469    }
470
471    private void setRoad(Object value, TrainManualBuildItem mbi) {
472        String road = (String) ((JComboBox<?>) value).getSelectedItem();
473        mbi.setRoadName(road);
474    }
475
476    private void setLoad(Object value, TrainManualBuildItem mbi) {
477        String load = (String) ((JComboBox<?>) value).getSelectedItem();
478        mbi.setLoadName(load);
479    }
480
481    private void setRouteLocation(Object value, TrainManualBuildItem mbi) {
482        mbi.setLocationTrack(null);
483        RouteLocation rl = (RouteLocation) ((JComboBox<?>) value).getSelectedItem();
484        mbi.setRouteLocation(rl);
485    }
486
487    private void setLocTrack(Object value, TrainManualBuildItem mbi) {
488        Track track = (Track) ((JComboBox<?>) value).getSelectedItem();
489        mbi.setLocationTrack(track);
490    }
491
492    private void setDestination(Object value, TrainManualBuildItem mbi) {
493        mbi.setDestinationTrack(null);
494        Location dest = (Location) ((JComboBox<?>) value).getSelectedItem();
495        mbi.setDestination(dest);
496    }
497
498    private void setDestTrack(Object value, TrainManualBuildItem mbi) {
499        Track track = (Track) ((JComboBox<?>) value).getSelectedItem();
500        mbi.setDestinationTrack(track);
501    }
502
503    private void setPickupDay(Object value, TrainManualBuildItem mbi) {
504        Object obj = ((JComboBox<?>) value).getSelectedItem();
505        if (obj == null) {
506            mbi.setTrainScheduleId(TrainManualBuildItem.NONE);
507        } else if (obj.getClass().equals(TrainSchedule.class)) {
508            mbi.setTrainScheduleId(((TrainSchedule) obj).getId());
509        }
510    }
511
512    private void setCount(Object value, TrainManualBuildItem mbi) {
513        mbi.setCount((int) value);
514    }
515
516    private void setWarn(Object value, TrainManualBuildItem mbi) {
517        mbi.setWarnEnabled(((Boolean) value).booleanValue());
518        if (mbi.isWarnEnabled()) {
519            mbi.setFailEnabled(false);
520        }
521    }
522
523    private void setFail(Object value, TrainManualBuildItem mbi) {
524        mbi.setFailEnabled(((Boolean) value).booleanValue());
525        if (mbi.isFailEnabled()) {
526            mbi.setWarnEnabled(false);
527        }
528    }
529
530    private void moveUpManualBuildItem(int row) {
531        log.debug("move schedule item up");
532        _manualBuild.moveItemUp(_list.get(row));
533    }
534
535    private void moveDownManualBuildItem(int row) {
536        log.debug("move schedule item down");
537        _manualBuild.moveItemDown(_list.get(row));
538    }
539
540    private void deleteManualBuildItem(int row) {
541        log.debug("Delete schedule item");
542        _manualBuild.deleteItem(_list.get(row));
543    }
544
545    // remove route locations that don't service the car's type
546    private void filterRouteLocations(JComboBox<RouteLocation> cb, String carType) {
547        for (int i = 1; i < cb.getItemCount(); i++) {
548            RouteLocation rl = cb.getItemAt(i);
549            if (!carType.equals(NONE) && !rl.getLocation().acceptsTypeName(carType)) {
550                cb.removeItem(rl);
551                i--;
552            }
553        }
554    }
555
556    // remove destinations that don't service the car's type
557    private void filterLocations(JComboBox<Location> cb, String carType) {
558        for (int i = 1; i < cb.getItemCount(); i++) {
559            Location loc = cb.getItemAt(i);
560            if (!carType.equals(NONE) && !loc.acceptsTypeName(carType)) {
561                cb.removeItem(loc);
562                i--;
563            }
564        }
565    }
566
567    // remove tracks that don't service the car's type, road, or load
568    private void filterTracks(Location loc, JComboBox<Track> cb, String carType, String carRoad, String carLoad) {
569        List<Track> tracks = loc.getTracksList();
570        for (Track track : tracks) {
571            if (track.isStaging() ||
572                    (!carType.equals(NONE) && !track.isTypeNameAccepted(carType)) ||
573                    (!carRoad.equals(NONE) && !track.isRoadNameAccepted(carRoad)) ||
574                    (!carLoad.equals(NONE) && !track.isLoadNameAndCarTypeAccepted(carLoad, carType))) {
575                cb.removeItem(track);
576            }
577        }
578    }
579
580    @Override
581    public void propertyChange(PropertyChangeEvent e) {
582        if (Control.SHOW_PROPERTY) {
583            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
584                    .getNewValue());
585        }
586        if (e.getPropertyName().equals(TrainManualBuild.LISTCHANGE_CHANGED_PROPERTY)) {
587            updateList();
588            fireTableDataChanged();
589        }
590        if (e.getSource().getClass().equals(TrainManualBuildItem.class)) {
591            TrainManualBuildItem item = (TrainManualBuildItem) e.getSource();
592            int row = _list.indexOf(item);
593            if (Control.SHOW_PROPERTY) {
594                log.debug("Update table row: {}", row);
595            }
596            if (row >= 0) {
597                fireTableRowsUpdated(row, row);
598            }
599        }
600    }
601
602    private void removePropertyChangeManualBuildItems() {
603        for (TrainManualBuildItem mbi : _list) {
604            mbi.removePropertyChangeListener(this);
605            if (mbi.getDestination() != null) {
606                mbi.getDestination().removePropertyChangeListener(this);
607            }
608            if (mbi.getDestinationTrack() != null) {
609                mbi.getDestinationTrack().removePropertyChangeListener(this);
610            }
611        }
612    }
613
614    public void dispose() {
615        if (_manualBuild != null) {
616            removePropertyChangeManualBuildItems();
617            _manualBuild.removePropertyChangeListener(this);
618        }
619    }
620
621    private static final Logger log = LoggerFactory.getLogger(TrainManualBuildTableModel.class);
622}