001package jmri.jmrit.operations.rollingstock.cars;
002
003import java.util.*;
004
005import javax.swing.JComboBox;
006
007import org.jdom2.Attribute;
008import org.jdom2.Element;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012import jmri.InstanceManager;
013import jmri.InstanceManagerAutoDefault;
014import jmri.jmrit.operations.OperationsPanel;
015import jmri.jmrit.operations.rollingstock.RollingStockAttribute;
016import jmri.jmrit.operations.trains.TrainManifestHeaderText;
017import jmri.jmrit.operations.trains.trainbuilder.TrainCommon;
018
019/**
020 * Represents the loads that cars can have.
021 *
022 * @author Daniel Boudreau Copyright (C) 2008, 2014
023 */
024public class CarLoads extends RollingStockAttribute implements InstanceManagerAutoDefault {
025
026    protected Hashtable<String, List<CarLoad>> listCarLoads = new Hashtable<>();
027    protected String _emptyName = Bundle.getMessage("EmptyCar");
028    protected String _loadName = Bundle.getMessage("LoadedCar");
029
030    public static final String NONE = ""; // NOI18N
031
032    // for property change
033    public static final String LOAD_CHANGED_PROPERTY = "CarLoads_Load"; // NOI18N
034    public static final String LOAD_TYPE_CHANGED_PROPERTY = "CarLoads_Load_Type"; // NOI18N
035    public static final String LOAD_PRIORITY_CHANGED_PROPERTY = "CarLoads_Load_Priority"; // NOI18N
036    public static final String LOAD_NAME_CHANGED_PROPERTY = "CarLoads_Name"; // NOI18N
037    public static final String LOAD_COMMENT_CHANGED_PROPERTY = "CarLoads_Load_Comment"; // NOI18N
038    public static final String LOAD_HAZARDOUS_CHANGED_PROPERTY = "CarLoads_Load_Hazardous"; // NOI18N
039
040    public CarLoads() {
041    }
042
043    /**
044     * Add a car type with specific loads
045     *
046     * @param type car type
047     */
048    public void addType(String type) {
049        listCarLoads.put(type, new ArrayList<>());
050    }
051
052    /**
053     * Replace a car type. Transfers load type, priority, isHardous, drop and
054     * load comments.
055     *
056     * @param oldType old car type
057     * @param newType new car type
058     */
059    public void replaceType(String oldType, String newType) {
060        List<String> names = getNames(oldType);
061        addType(newType);
062        for (String name : names) {
063            addName(newType, name);
064            setLoadType(newType, name, getLoadType(oldType, name));
065            setPriority(newType, name, getPriority(oldType, name));
066            setHazardous(newType, name, isHazardous(oldType, name));
067            setDropComment(newType, name, getDropComment(oldType, name));
068            setPickupComment(newType, name, getPickupComment(oldType, name));
069        }
070        listCarLoads.remove(oldType);
071    }
072
073    /**
074     * Gets the appropriate car loads for the car's type.
075     *
076     * @param type Car type
077     * @return JComboBox with car loads starting with empty string.
078     */
079    public JComboBox<String> getSelectComboBox(String type) {
080        JComboBox<String> box = new JComboBox<>();
081        box.addItem(NONE);
082        for (String load : getNames(type)) {
083            box.addItem(load);
084        }
085        return box;
086    }
087
088    /**
089     * Gets the appropriate car loads for the car's type.
090     *
091     * @param type Car type
092     * @return JComboBox with car loads.
093     */
094    public JComboBox<String> getComboBox(String type) {
095        JComboBox<String> box = new JComboBox<>();
096        updateComboBox(type, box);
097        return box;
098
099    }
100
101    /**
102     * Gets a ComboBox with the available priorities
103     *
104     * @return JComboBox with car priorities.
105     */
106    public JComboBox<String> getPriorityComboBox() {
107        JComboBox<String> box = new JComboBox<>();
108        box.addItem(CarLoad.PRIORITY_LOW);
109        box.addItem(CarLoad.PRIORITY_MEDIUM);
110        box.addItem(CarLoad.PRIORITY_HIGH);
111        return box;
112    }
113
114    public JComboBox<String> getHazardousComboBox() {
115        JComboBox<String> box = new JComboBox<>();
116        box.addItem(Bundle.getMessage("ButtonNo"));
117        box.addItem(Bundle.getMessage("ButtonYes"));
118        return box;
119    }
120
121    /**
122     * Gets a ComboBox with the available load types: empty and load
123     *
124     * @return JComboBox with load types: LOAD_TYPE_EMPTY and LOAD_TYPE_LOAD
125     */
126    public JComboBox<String> getLoadTypesComboBox() {
127        JComboBox<String> box = new JComboBox<>();
128        box.addItem(CarLoad.LOAD_TYPE_EMPTY);
129        box.addItem(CarLoad.LOAD_TYPE_LOAD);
130        OperationsPanel.padComboBox(box);
131        return box;
132    }
133
134    /**
135     * Gets a sorted list of load names for a given car type
136     *
137     * @param type car type
138     * @return list of load names
139     */
140    public List<String> getNames(String type) {
141        List<String> names = new ArrayList<>();
142        if (type == null) {
143            names.add(getDefaultEmptyName());
144            names.add(getDefaultLoadName());
145            return names;
146        }
147        List<CarLoad> loads = listCarLoads.get(type);
148        if (loads == null) {
149            addType(type);
150            loads = listCarLoads.get(type);
151        }
152        if (loads.isEmpty()) {
153            loads.add(new CarLoad(getDefaultEmptyName()));
154            loads.add(new CarLoad(getDefaultLoadName()));
155        }
156        for (CarLoad carLoad : loads) {
157            names.add(carLoad.getName());
158        }
159        java.util.Collections.sort(names);
160        return names;
161    }
162
163    /**
164     * Add a load name for the car type.
165     *
166     * @param type car type.
167     * @param name load name.
168     */
169    public void addName(String type, String name) {
170        // don't add if name already exists
171        if (containsName(type, name)) {
172            return;
173        }
174        List<CarLoad> loads = listCarLoads.get(type);
175        if (loads == null) {
176            log.debug("car type ({}) does not exist", type);
177            return;
178        }
179        loads.add(new CarLoad(name));
180        maxNameLength = 0; // reset maximum name length
181        setDirtyAndFirePropertyChange(LOAD_CHANGED_PROPERTY, null, name);
182    }
183
184    public void deleteName(String type, String name) {
185        List<CarLoad> loads = listCarLoads.get(type);
186        if (loads == null) {
187            log.debug("car type ({}) does not exist", type);
188            return;
189        }
190        for (CarLoad cl : loads) {
191            if (cl.getName().equals(name)) {
192                loads.remove(cl);
193                break;
194            }
195        }
196        maxNameLength = 0; // reset maximum name length
197        setDirtyAndFirePropertyChange(LOAD_CHANGED_PROPERTY, name, null);
198    }
199
200    /**
201     * Determines if a car type can have a specific load name.
202     *
203     * @param type car type.
204     * @param name load name.
205     * @return true if car can have this load name.
206     */
207    public boolean containsName(String type, String name) {
208        List<String> names = getNames(type);
209        return names.contains(name);
210    }
211
212    public void updateComboBox(String type, JComboBox<String> box) {
213        box.removeAllItems();
214        List<String> names = getNames(type);
215        for (String name : names) {
216            box.addItem(name);
217        }
218        OperationsPanel.padComboBox(box, getMaxNameLength() + 1);
219    }
220
221    /**
222     * Update a JComboBox with all load names for every type of car.
223     *
224     * @param box the combo box to update
225     */
226    @Override
227    public void updateComboBox(JComboBox<String> box) {
228        box.removeAllItems();
229        List<String> names = new ArrayList<>();
230        for (String type : InstanceManager.getDefault(CarTypes.class).getNames()) {
231            for (String load : getNames(type)) {
232                if (!names.contains(load)) {
233                    names.add(load);
234                }
235            }
236        }
237        java.util.Collections.sort(names);
238        for (String load : names) {
239            box.addItem(load);
240        }
241    }
242
243    public void updateRweComboBox(String type, JComboBox<String> box) {
244        box.removeAllItems();
245        List<String> loads = getNames(type);
246        for (String name : loads) {
247            if (getLoadType(type, name).equals(CarLoad.LOAD_TYPE_EMPTY)) {
248                box.addItem(name);
249            }
250        }
251    }
252
253    public void updateRwlComboBox(String type, JComboBox<String> box) {
254        box.removeAllItems();
255        List<String> loads = getNames(type);
256        for (String name : loads) {
257            if (getLoadType(type, name).equals(CarLoad.LOAD_TYPE_LOAD)) {
258                box.addItem(name);
259            }
260        }
261    }
262
263    public void replaceName(String type, String oldName, String newName) {
264        addName(type, newName);
265        deleteName(type, oldName);
266        setDirtyAndFirePropertyChange(LOAD_NAME_CHANGED_PROPERTY, oldName, newName);
267    }
268
269    public String getDefaultLoadName() {
270        return _loadName;
271    }
272
273    public void setDefaultLoadName(String name) {
274        String old = _loadName;
275        _loadName = name;
276        setDirtyAndFirePropertyChange(LOAD_NAME_CHANGED_PROPERTY, old, name);
277    }
278
279    public String getDefaultEmptyName() {
280        return _emptyName;
281    }
282
283    public void setDefaultEmptyName(String name) {
284        String old = _emptyName;
285        _emptyName = name;
286        setDirtyAndFirePropertyChange(LOAD_NAME_CHANGED_PROPERTY, old, name);
287    }
288
289    /**
290     * Sets the load type, empty or load.
291     *
292     * @param type     car type.
293     * @param name     load name.
294     * @param loadType load type: LOAD_TYPE_EMPTY or LOAD_TYPE_LOAD.
295     */
296    public void setLoadType(String type, String name, String loadType) {
297        List<CarLoad> loads = listCarLoads.get(type);
298        if (loads != null) {
299            for (CarLoad cl : loads) {
300                if (cl.getName().equals(name)) {
301                    String oldType = cl.getLoadType();
302                    cl.setLoadType(loadType);
303                    if (!oldType.equals(loadType)) {
304                        setDirtyAndFirePropertyChange(LOAD_TYPE_CHANGED_PROPERTY, oldType, loadType);
305                    }
306                }
307            }
308        }
309    }
310
311    /**
312     * Get the load type, empty or load.
313     *
314     * @param type car type.
315     * @param name load name.
316     * @return load type, LOAD_TYPE_EMPTY or LOAD_TYPE_LOAD.
317     */
318    public String getLoadType(String type, String name) {
319        if (!containsName(type, name)) {
320            if (name != null && name.equals(getDefaultEmptyName())) {
321                return CarLoad.LOAD_TYPE_EMPTY;
322            }
323            return CarLoad.LOAD_TYPE_LOAD;
324        }
325        List<CarLoad> loads = listCarLoads.get(type);
326        for (CarLoad cl : loads) {
327            if (cl.getName().equals(name)) {
328                return cl.getLoadType();
329            }
330        }
331        return Bundle.getMessage("ErrorTitle"); // NOI18N
332    }
333
334    /**
335     * Sets a loads priority.
336     *
337     * @param type     car type.
338     * @param name     load name.
339     * @param priority load priority, PRIORITY_LOW, PRIORITY_MEDIUM or
340     *                 PRIORITY_HIGH.
341     */
342    public void setPriority(String type, String name, String priority) {
343        List<CarLoad> loads = listCarLoads.get(type);
344        if (loads != null) {
345            for (CarLoad cl : loads) {
346                if (cl.getName().equals(name)) {
347                    String oldPriority = cl.getPriority();
348                    cl.setPriority(priority);
349                    if (!oldPriority.equals(priority)) {
350                        setDirtyAndFirePropertyChange(LOAD_PRIORITY_CHANGED_PROPERTY, oldPriority, priority);
351                    }
352                }
353            }
354        }
355    }
356
357    /**
358     * Get's a load's priority.
359     *
360     * @param type car type.
361     * @param name load name.
362     * @return load priority, PRIORITY_LOW, PRIORITY_MEDIUM or PRIORITY_HIGH.
363     */
364    public String getPriority(String type, String name) {
365        if (!containsName(type, name)) {
366            return CarLoad.PRIORITY_LOW;
367        }
368        List<CarLoad> loads = listCarLoads.get(type);
369        if (loads != null) {
370            for (CarLoad cl : loads) {
371                if (cl.getName().equals(name)) {
372                    return cl.getPriority();
373                }
374            }
375        }
376        return Bundle.getMessage("ErrorTitle"); // NOI18N
377    }
378
379    public void setHazardous(String type, String name, boolean isHazardous) {
380        List<CarLoad> loads = listCarLoads.get(type);
381        if (loads != null) {
382            for (CarLoad cl : loads) {
383                if (cl.getName().equals(name)) {
384                    boolean oldIsHazardous = cl.isHazardous();
385                    cl.setHazardous(isHazardous);
386                    if (oldIsHazardous != isHazardous) {
387                        setDirtyAndFirePropertyChange(LOAD_HAZARDOUS_CHANGED_PROPERTY, oldIsHazardous, isHazardous);
388                    }
389                }
390            }
391        }
392    }
393
394    public boolean isHazardous(String type, String name) {
395        if (!containsName(type, name)) {
396            return false;
397        }
398        List<CarLoad> loads = listCarLoads.get(type);
399        for (CarLoad cl : loads) {
400            if (cl.getName().equals(name)) {
401                return cl.isHazardous();
402            }
403        }
404        return false;
405    }
406
407    /**
408     * Sets the comment for a car type's load
409     * 
410     * @param type    the car type
411     * @param name    the load name
412     * @param comment the comment
413     */
414    public void setPickupComment(String type, String name, String comment) {
415        if (!containsName(type, name)) {
416            return;
417        }
418        List<CarLoad> loads = listCarLoads.get(type);
419        if (loads != null) {
420            for (CarLoad cl : loads) {
421                if (cl.getName().equals(name)) {
422                    String oldComment = cl.getPickupComment();
423                    cl.setPickupComment(comment);
424                    if (!oldComment.equals(comment)) {
425                        maxCommentLength = 0;
426                        setDirtyAndFirePropertyChange(LOAD_COMMENT_CHANGED_PROPERTY, oldComment, comment);
427                    }
428                }
429            }
430        }
431    }
432
433    public String getPickupComment(String type, String name) {
434        if (!containsName(type, name)) {
435            return NONE;
436        }
437        List<CarLoad> loads = listCarLoads.get(type);
438        for (CarLoad cl : loads) {
439            if (cl.getName().equals(name)) {
440                return cl.getPickupComment();
441            }
442        }
443        return NONE;
444    }
445
446    public void setDropComment(String type, String name, String comment) {
447        if (!containsName(type, name)) {
448            return;
449        }
450        List<CarLoad> loads = listCarLoads.get(type);
451        if (loads != null) {
452            for (CarLoad cl : loads) {
453                if (cl.getName().equals(name)) {
454                    String oldComment = cl.getDropComment();
455                    cl.setDropComment(comment);
456                    if (!oldComment.equals(comment)) {
457                        maxCommentLength = 0;
458                        setDirtyAndFirePropertyChange(LOAD_COMMENT_CHANGED_PROPERTY, oldComment, comment);
459                    }
460                }
461            }
462        }
463    }
464
465    public String getDropComment(String type, String name) {
466        if (!containsName(type, name)) {
467            return NONE;
468        }
469        List<CarLoad> loads = listCarLoads.get(type);
470        for (CarLoad cl : loads) {
471            if (cl.getName().equals(name)) {
472                return cl.getDropComment();
473            }
474        }
475        return NONE;
476    }
477
478    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SLF4J_FORMAT_SHOULD_BE_CONST",
479            justification = "I18N of Info Message")
480    @Override
481    public int getMaxNameLength() {
482        if (maxNameLength == 0) {
483            maxName = "";
484            maxNameLength = MIN_NAME_LENGTH;
485            String carTypeName = "";
486            Enumeration<String> en = listCarLoads.keys();
487            while (en.hasMoreElements()) {
488                String cartype = en.nextElement();
489                List<CarLoad> loads = listCarLoads.get(cartype);
490                for (CarLoad load : loads) {
491                    if (load.getName().split(TrainCommon.HYPHEN)[0].length() > maxNameLength) {
492                        maxName = load.getName().split(TrainCommon.HYPHEN)[0];
493                        maxNameLength = load.getName().split(TrainCommon.HYPHEN)[0].length();
494                        carTypeName = cartype;
495                    }
496                }
497            }
498            log.info(Bundle.getMessage("InfoMaxLoad", maxName, maxNameLength, carTypeName));
499        }
500        return maxNameLength;
501    }
502
503    int maxCommentLength = 0;
504
505    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SLF4J_FORMAT_SHOULD_BE_CONST",
506            justification = "I18N of Info Message")
507    public int getMaxLoadCommentLength() {
508        if (maxCommentLength == 0) {
509            String maxComment = "";
510            String carTypeName = "";
511            String carLoadName = "";
512            Enumeration<String> en = listCarLoads.keys();
513            while (en.hasMoreElements()) {
514                String carType = en.nextElement();
515                List<CarLoad> loads = listCarLoads.get(carType);
516                for (CarLoad load : loads) {
517                    if (load.getDropComment().length() > maxCommentLength) {
518                        maxComment = load.getDropComment();
519                        maxCommentLength = load.getDropComment().length();
520                        carTypeName = carType;
521                        carLoadName = load.getName();
522                    }
523                    if (load.getPickupComment().length() > maxCommentLength) {
524                        maxComment = load.getPickupComment();
525                        maxCommentLength = load.getPickupComment().length();
526                        carTypeName = carType;
527                        carLoadName = load.getName();
528                    }
529                }
530            }
531            if (maxCommentLength < TrainManifestHeaderText.getStringHeader_Drop_Comment().length()) {
532                maxCommentLength = TrainManifestHeaderText.getStringHeader_Drop_Comment().length();
533            }
534            if (maxCommentLength < TrainManifestHeaderText.getStringHeader_Pickup_Comment().length()) {
535                maxCommentLength = TrainManifestHeaderText.getStringHeader_Pickup_Comment().length();
536            }
537            if (!maxComment.isBlank()) {
538                log.info(Bundle.getMessage("InfoMaxLoadMessage", maxComment, maxCommentLength,
539                        carTypeName, carLoadName));
540            }
541        }
542        return maxCommentLength;
543    }
544
545    private List<CarLoad> getSortedList(String type) {
546        List<CarLoad> loads = listCarLoads.get(type);
547        List<String> names = getNames(type);
548        List<CarLoad> out = new ArrayList<>();
549
550        // return a list sorted by load name
551        for (String name : names) {
552            for (CarLoad carLoad : loads) {
553                if (name.equals(carLoad.getName())) {
554                    out.add(carLoad);
555                    break;
556                }
557            }
558        }
559        return out;
560    }
561
562    @SuppressWarnings("unchecked")
563    public Hashtable<String, List<CarLoad>> getList() {
564        return (Hashtable<String, List<CarLoad>>) listCarLoads.clone();
565    }
566
567    @Override
568    public void dispose() {
569        listCarLoads.clear();
570        setDefaultEmptyName(Bundle.getMessage("EmptyCar"));
571        setDefaultLoadName(Bundle.getMessage("LoadedCar"));
572        super.dispose();
573    }
574
575    /**
576     * Create an XML element to represent this Entry. This member has to remain
577     * synchronized with the detailed DTD in operations-cars.dtd.
578     *
579     * @param root The common Element for operations-cars.dtd.
580     */
581    public void store(Element root) {
582        Element values = new Element(Xml.LOADS);
583        // store default load and empty
584        Element defaults = new Element(Xml.DEFAULTS);
585        defaults.setAttribute(Xml.EMPTY, getDefaultEmptyName());
586        defaults.setAttribute(Xml.LOAD, getDefaultLoadName());
587        values.addContent(defaults);
588        // store loads based on car types
589        String[] carTypeNames = InstanceManager.getDefault(CarTypes.class).getNames();
590        for (String carType : carTypeNames) {
591            if (!listCarLoads.containsKey(carType)) {
592                continue;
593            }
594            List<CarLoad> loads = getSortedList(carType);
595            Element xmlLoad = new Element(Xml.LOAD);
596            xmlLoad.setAttribute(Xml.TYPE, carType);
597            boolean mustStore = false; // only store loads that aren't the defaults
598            for (CarLoad load : loads) {
599                // don't store the defaults / low priority / not hazardous / no comment
600                if ((load.getName().equals(getDefaultEmptyName()) || load.getName().equals(getDefaultLoadName())) &&
601                        load.getPriority().equals(CarLoad.PRIORITY_LOW) &&
602                        !load.isHazardous() &&
603                        load.getPickupComment().equals(CarLoad.NONE) &&
604                        load.getDropComment().equals(CarLoad.NONE)) {
605                    continue;
606                }
607                Element xmlCarLoad = new Element(Xml.CAR_LOAD);
608                xmlCarLoad.setAttribute(Xml.NAME, load.getName());
609                if (!load.getPriority().equals(CarLoad.PRIORITY_LOW)) {
610                    xmlCarLoad.setAttribute(Xml.PRIORITY, load.getPriority());
611                    mustStore = true; // must store
612                }
613                if (load.isHazardous()) {
614                    xmlCarLoad.setAttribute(Xml.HAZARDOUS, load.isHazardous() ? Xml.TRUE : Xml.FALSE);
615                    mustStore = true; // must store
616                }
617                if (!load.getPickupComment().equals(CarLoad.NONE)) {
618                    xmlCarLoad.setAttribute(Xml.PICKUP_COMMENT, load.getPickupComment());
619                    mustStore = true; // must store
620                }
621                if (!load.getDropComment().equals(CarLoad.NONE)) {
622                    xmlCarLoad.setAttribute(Xml.DROP_COMMENT, load.getDropComment());
623                    mustStore = true; // must store
624                }
625                xmlCarLoad.setAttribute(Xml.LOAD_TYPE, load.getLoadType());
626                xmlLoad.addContent(xmlCarLoad);
627            }
628            if (loads.size() > 2 || mustStore) {
629                values.addContent(xmlLoad);
630            }
631        }
632        root.addContent(values);
633    }
634
635    public void load(Element e) {
636        if (e.getChild(Xml.LOADS) == null) {
637            return;
638        }
639        Attribute a;
640        Element defaults = e.getChild(Xml.LOADS).getChild(Xml.DEFAULTS);
641        if (defaults != null) {
642            if ((a = defaults.getAttribute(Xml.LOAD)) != null) {
643                _loadName = a.getValue();
644            }
645            if ((a = defaults.getAttribute(Xml.EMPTY)) != null) {
646                _emptyName = a.getValue();
647            }
648        }
649        List<Element> eLoads = e.getChild(Xml.LOADS).getChildren(Xml.LOAD);
650        log.debug("readFile sees {} car loads", eLoads.size());
651        for (Element eLoad : eLoads) {
652            if ((a = eLoad.getAttribute(Xml.TYPE)) != null) {
653                String type = a.getValue();
654                addType(type);
655                // old style had a list of names
656                if ((a = eLoad.getAttribute(Xml.NAMES)) != null) {
657                    String names = a.getValue();
658                    String[] loadNames = names.split("%%");// NOI18N
659                    Arrays.sort(loadNames);
660                    log.debug("Car load type: {} loads: {}", type, names);
661                    // addName puts new items at the start, so reverse load
662                    for (int j = loadNames.length; j > 0;) {
663                        addName(type, loadNames[--j]);
664                    }
665                }
666                // new style load and comments
667                List<Element> eCarLoads = eLoad.getChildren(Xml.CAR_LOAD);
668                log.debug("{} car loads for type: {}", eCarLoads.size(), type);
669                for (Element eCarLoad : eCarLoads) {
670                    if ((a = eCarLoad.getAttribute(Xml.NAME)) != null) {
671                        String name = a.getValue();
672                        if (name.trim().equals(TrainCommon.HYPHEN)) {
673                            log.error("Illegal load name ({}) for type ({})", name, type);
674                        } else {
675                            addName(type, name);
676                        }
677                        if ((a = eCarLoad.getAttribute(Xml.PRIORITY)) != null) {
678                            setPriority(type, name, a.getValue());
679                        }
680                        if ((a = eCarLoad.getAttribute(Xml.HAZARDOUS)) != null) {
681                            setHazardous(type, name, a.getValue().equals(Xml.TRUE));
682                        }
683                        if ((a = eCarLoad.getAttribute(Xml.PICKUP_COMMENT)) != null) {
684                            setPickupComment(type, name, a.getValue());
685                        }
686                        if ((a = eCarLoad.getAttribute(Xml.DROP_COMMENT)) != null) {
687                            setDropComment(type, name, a.getValue());
688                        }
689                        if ((a = eCarLoad.getAttribute(Xml.LOAD_TYPE)) != null) {
690                            setLoadType(type, name, a.getValue());
691                        }
692                    }
693                }
694            }
695        }
696    }
697
698    protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) {
699        // Set dirty
700        InstanceManager.getDefault(CarManagerXml.class).setDirty(true);
701        super.firePropertyChange(p, old, n);
702    }
703
704    private static final Logger log = LoggerFactory.getLogger(CarLoads.class);
705
706}