001package jmri.jmrit.symbolicprog.tabbedframe;
002
003import java.util.ArrayList;
004import java.util.List;
005import javax.swing.JLabel;
006import jmri.InstanceManager;
007import jmri.Programmer;
008import jmri.jmrit.decoderdefn.DecoderFile;
009import jmri.jmrit.decoderdefn.DecoderIndexFile;
010import jmri.jmrit.roster.RosterEntry;
011import jmri.jmrit.symbolicprog.CvTableModel;
012import jmri.jmrit.symbolicprog.ResetTableModel;
013import jmri.jmrit.symbolicprog.SymbolicProgBundle;
014import jmri.jmrit.symbolicprog.VariableTableModel;
015import org.jdom2.*;
016import java.io.*;
017import org.slf4j.Logger;
018import org.slf4j.LoggerFactory;
019
020/**
021 * Interface for the container of a set of PaneProgPanes. The panes use services
022 * provided here to work with buttons and the busy cursor.
023 * <p>
024 * TODO: Several methods are copied from PaneProgFrame and should be refactored
025 * No programmer support yet No glass pane support Need better support for
026 * visible/non-visible panes Special panes (Roster entry, attributes, graphics)
027 * not included
028 *
029 * @see apps.gui3.dp3.DecoderPro3Window
030 *
031 * @author Bob Jacobsen Copyright (C) 2010
032 */
033public class PaneSet {
034
035    List<PaneProgPane> paneList = new ArrayList<>();
036    PaneContainer container;
037    Programmer mProgrammer;
038    CvTableModel cvModel = null;
039    VariableTableModel variableModel;
040    ResetTableModel resetModel = null;
041    JLabel progStatus = new JLabel(SymbolicProgBundle.getMessage("StateIdle"));
042
043    /**
044     * The 'model' element representing the decoder type
045     */
046    Element modelElem = null;
047
048    public PaneSet(PaneContainer container, RosterEntry re, Programmer programmer) {
049        this.container = container;
050        this.mProgrammer = programmer;
051
052        cvModel = new CvTableModel(progStatus, mProgrammer);
053
054        variableModel = new VariableTableModel(progStatus, new String[]{"Name", "Value"},
055                cvModel);
056
057        resetModel = new ResetTableModel(progStatus, mProgrammer);
058
059        if (re.getFileName() != null) {
060            // set the loco file name in the roster entry
061            re.readFile();  // read, but don't yet process
062        }
063
064        // load from decoder file
065        loadDecoderFromLoco(re);
066
067        // finally fill the Variable and CV values from the specific loco file
068        if (re.getFileName() != null) {
069            re.loadCvModel(variableModel, cvModel);
070        }
071    }
072
073    // copied from PaneProgFrame
074    protected void loadDecoderFromLoco(RosterEntry r) {
075        // get a DecoderFile from the locomotive xml
076        String decoderModel = r.getDecoderModel();
077        String decoderFamily = r.getDecoderFamily();
078        if (log.isDebugEnabled()) {
079            log.debug("selected loco uses decoder {} {}", decoderFamily, decoderModel);
080        }
081        // locate a decoder like that.
082        List<DecoderFile> l = InstanceManager.getDefault(DecoderIndexFile.class).matchingDecoderList(null, decoderFamily, null, null, null, decoderModel);
083        if (log.isDebugEnabled()) {
084            log.debug("found {} matches", l.size());
085        }
086        if (l.size() == 0) {
087            log.debug("Loco uses {} {} decoder, but no such decoder defined", decoderFamily, decoderModel);
088            // fall back to use just the decoder name, not family
089            l = InstanceManager.getDefault(DecoderIndexFile.class).matchingDecoderList(null, null, null, null, null, decoderModel);
090            if (log.isDebugEnabled()) {
091                log.debug("found {} matches without family key", l.size());
092            }
093        }
094        if (l.size() > 0) {
095            DecoderFile d = l.get(0);
096            loadDecoderFile(d, r);
097        } else {
098            if (decoderModel.equals("")) {
099                log.debug("blank decoderModel requested, so nothing loaded");
100            } else {
101                log.warn("no matching \"{}\" decoder found for loco, no decoder info loaded", decoderModel);
102            }
103        }
104    }
105
106    protected void loadDecoderFile(DecoderFile df, RosterEntry re) {
107        if (df == null) {
108            log.warn("loadDecoder file invoked with null object");
109            return;
110        }
111        if (log.isDebugEnabled()) {
112            log.debug("loadDecoderFile from {} {}", DecoderFile.fileLocation, df.getFileName());
113        }
114
115        try {
116            decoderRoot = df.rootFromName(DecoderFile.fileLocation + df.getFileName());
117        } catch (JDOMException | IOException e) {
118            log.error("Exception while loading decoder XML file: {}", df.getFileName(), e);
119        }
120        // load variables from decoder tree
121        df.getProductID();
122        df.loadVariableModel(decoderRoot.getChild("decoder"), variableModel);
123
124        // load reset from decoder tree
125        df.loadResetModel(decoderRoot.getChild("decoder"), resetModel);
126
127        // load function names
128        re.loadFunctions(decoderRoot.getChild("decoder").getChild("family").getChild("functionlabels"));
129
130//         // get the showEmptyPanes attribute, if yes/no update our state
131//         if (decoderRoot.getAttribute("showEmptyPanes") != null) {
132//             if (log.isDebugEnabled()) log.debug("Found in decoder "+decoderRoot.getAttribute("showEmptyPanes").getValue());
133//             if (decoderRoot.getAttribute("showEmptyPanes").getValue().equals("yes"))
134//                 setShowEmptyPanes(true);
135//             else if (decoderRoot.getAttribute("showEmptyPanes").getValue().equals("no"))
136//                 setShowEmptyPanes(false);
137//             // leave alone for "default" value
138//             if (log.isDebugEnabled()) log.debug("result "+getShowEmptyPanes());
139//         }
140        // save the pointer to the model element
141        modelElem = df.getModelElement();
142    }
143    Element decoderRoot = null;
144
145    /**
146     * Create a set of panes from a programmer definition and roster entry
147     *
148     * @param root Root element of programmer XML definition
149     * @param r    Locomotive to load from
150     */
151    public void makePanes(Element root, RosterEntry r) {
152        // check for "programmer" element at start
153        Element base;
154        if ((base = root.getChild("programmer")) == null) {
155            log.error("xml file top element is not programmer");
156            return;
157        }
158
159        List<Element> paneList = base.getChildren("pane");
160
161        if (log.isDebugEnabled()) {
162            log.debug("will process {} pane definitions", paneList.size());
163        }
164
165        for (Element e : paneList) {
166            // load each pane
167            String name = e.getAttribute("name").getValue();
168            newPane(name, e, modelElem, r);
169        }
170    }
171
172    /**
173     * Create a single pane from a "pane" element in programmer or decoder
174     * definition
175     * @param name pane name.
176     * @param pane pane element.
177     * @param modelElem model element.
178     * @param r roster entry.
179     */
180    public void newPane(String name, Element pane, Element modelElem, RosterEntry r) {
181        if (log.isDebugEnabled()) {
182            log.debug("newPane {}", name);
183        }
184        // create a panel to hold columns
185        PaneProgPane p = new PaneProgPane(container, name, pane, cvModel, variableModel, modelElem, r);
186
187        // and remember it for programming
188        paneList.add(p);
189    }
190
191    public List<PaneProgPane> getList() {
192        return paneList;
193    }
194
195    /**
196     * Store current content to file.
197     * @param re roster entry to store.
198     */
199    public void storeFile(RosterEntry re) {
200        // set up file write
201        re.ensureFilenameExists();
202
203        // write the RosterEntry to its file
204        re.writeFile(cvModel, variableModel);
205    }
206
207    private final static Logger log = LoggerFactory.getLogger(PaneSet.class);
208}