001package jmri.jmrit.operations.rollingstock;
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.beans.PropertyChangeSupport;
013import jmri.jmrit.operations.OperationsPanel;
014import jmri.jmrit.operations.trains.TrainCommon;
015
016/**
017 * Represents an attribute a rolling stock can have. Some attributes are length,
018 * color, type, load, road, owner, model etc.
019 *
020 * @author Daniel Boudreau Copyright (C) 2014
021 *
022 */
023public abstract class RollingStockAttribute extends PropertyChangeSupport {
024
025    protected static final int MIN_NAME_LENGTH = 1;
026
027    public RollingStockAttribute() {
028    }
029
030    public void dispose() {
031        list.clear();
032        //TODO The removal of listeners causes the tests to fail.
033        // Need to reload all listeners for the tests to work.
034        // Only tests currently call dispose()
035        // remove all listeners
036//  for (java.beans.PropertyChangeListener p : pcs.getPropertyChangeListeners())
037//   pcs.removePropertyChangeListener(p);
038    }
039
040    protected List<String> list = new ArrayList<>();
041
042    public String[] getNames() {
043        if (list.isEmpty()) {
044            list.addAll(Arrays.asList(getDefaultNames().split(",")));
045        }
046        return list.toArray(new String[0]);
047    }
048
049    protected String getDefaultNames() {
050        return "Error"; // overridden // NOI18N
051    }
052
053    public void setNames(String[] names) {
054        if (names.length > 0) {
055            Arrays.sort(names);
056            for (String name : names) {
057                if (!list.contains(name)) {
058                    list.add(name);
059                }
060            }
061        }
062    }
063
064    /**
065     * Performs number sort before adding to list
066     * @param lengths The set of strings to be ordered.
067     *
068     */
069    public void setValues(String[] lengths) {
070        if (lengths.length == 0) {
071            return;
072        }
073        try {
074            jmri.util.StringUtil.numberSort(lengths);
075        } catch (NumberFormatException e) {
076            log.error("lengths are not all numeric, list:");
077            for (int i = 0; i < lengths.length; i++) {
078                try {
079                    Integer.parseInt(lengths[i]);
080                    log.error("length {} = {}", i, lengths[i]);
081                } catch (NumberFormatException ee) {
082                    log.error("length {} = {} is not a valid number!", i, lengths[i]);
083                }
084            }
085        }
086        for (String length : lengths) {
087            if (!list.contains(length)) {
088                list.add(length);
089            }
090        }
091    }
092    
093    public void sort() {
094        java.util.Collections.sort(list);
095    }
096
097    public void addName(String name) {
098        if (name == null) {
099            return;
100        }
101        if (list.contains(name)) {
102            return;
103        }
104        list.add(name);
105        sort();
106        maxNameLength = 0; // reset maximum name length
107        maxNameSubStringLength = 0;
108    }
109
110    public void deleteName(String name) {
111        list.remove(name);
112        maxNameLength = 0; // reset maximum name length
113        maxNameSubStringLength = 0;
114    }
115
116    public boolean containsName(String name) {
117        return list.contains(name);
118    }
119
120    public JComboBox<String> getComboBox() {
121        JComboBox<String> box = new JComboBox<>();
122        updateComboBox(box);
123        OperationsPanel.padComboBox(box);
124        return box;
125    }
126
127    public void updateComboBox(JComboBox<String> box) {
128        box.removeAllItems();
129        for (String name : getNames()) {
130            box.addItem(name);
131        }
132    }
133
134    protected String maxName = "";
135    protected int maxNameLength = 0;
136    
137    public int getMaxNameLength() {
138        if (maxNameLength == 0) {
139            maxName = "";
140            maxNameLength = getMinNameLength();
141            for (String name : getNames()) {
142                if (name.length() > maxNameLength) {
143                    maxName = name;
144                    maxNameLength = name.length();
145                }
146            }
147        }
148        return maxNameLength;
149    }
150    
151    protected int maxNameSubStringLength = 0;
152    
153    public int getMaxNameSubStringLength() {
154        if (maxNameSubStringLength == 0) {
155            maxName = "";
156            maxNameSubStringLength = getMinNameLength();
157            for (String name : getNames()) {
158                String[] subString = name.split(TrainCommon.HYPHEN);
159                if (subString.length > 0 && subString[0].length() > maxNameSubStringLength) {
160                    maxName = name;
161                    maxNameSubStringLength = subString[0].length();
162                }
163            }
164        }
165        return maxNameSubStringLength;
166    }
167    
168    protected int getMinNameLength() {
169        return MIN_NAME_LENGTH;
170    }
171
172    /**
173     * Create an XML element to represent this Entry. This member has to remain
174     * synchronized with the detailed DTD in operations-cars.dtd and operations-engines.dtd.
175     * @param root Common Element for storage.
176     * @param eNames New format Element group name
177     * @param eName New format Element name
178     *
179     */
180    public void store(Element root, String eNames, String eName) {
181        Element names = new Element(eNames);
182        for (String name : getNames()) {
183            Element e = new Element(eName);
184            if (eName.equals(Xml.LENGTH)) {
185                e.setAttribute(new Attribute(Xml.VALUE, name));
186            } else {
187                e.setAttribute(new Attribute(Xml.NAME, name));
188            }
189            names.addContent(e);
190        }
191        root.addContent(names);
192    }
193
194    public void load(Element root, String eNames, String eName, String oldName) {
195        // new format using elements starting version 3.3.1
196        if (root.getChild(eNames) != null) {
197            List<Element> l = root.getChild(eNames).getChildren(eName);
198            Attribute a;
199            String[] names = new String[l.size()];
200            for (int i = 0; i < l.size(); i++) {
201                Element name = l.get(i);
202                if ((a = name.getAttribute(Xml.NAME)) != null) {
203                    names[i] = a.getValue();
204                }
205                // lengths use "VALUE"
206                if ((a = name.getAttribute(Xml.VALUE)) != null) {
207                    names[i] = a.getValue();
208                }
209
210            }
211            setNames(names);
212        } // try old format
213        else if (root.getChild(oldName) != null) {
214            String[] names = root.getChildText(oldName).split("%%"); // NOI18N
215            setNames(names);
216        }
217    }
218
219    private final static Logger log = LoggerFactory.getLogger(RollingStockAttribute.class);
220
221}