001package jmri.jmrit.roster;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.io.File;
006import java.util.Objects;
007import javax.swing.Icon;
008import javax.swing.JComboBox;
009import javax.swing.JFileChooser;
010
011import jmri.beans.BeanUtil;
012import jmri.jmrit.roster.rostergroup.RosterGroupSelector;
013import jmri.jmrit.roster.swing.RosterEntryComboBox;
014import jmri.util.swing.JmriJOptionPane;
015import jmri.util.swing.WindowInterface;
016
017/**
018 * Base class for Actions to copy, export and import RosterEntry objects.
019 * <p>
020 * Note that {@link DeleteRosterItemAction} is sufficiently different that it
021 * doesn't use this base class.
022 *
023 * @author Bob Jacobsen Copyright (C) 2001, 2002, 2007, 2008
024 * @see jmri.jmrit.XmlFile
025 */
026abstract public class AbstractRosterItemAction extends jmri.util.swing.JmriAbstractAction {
027
028    public AbstractRosterItemAction(String pName, Component pWho) {
029        super(pName);
030        mParent = pWho;
031    }
032
033    public AbstractRosterItemAction(String s, WindowInterface wi) {
034        super(s, wi);
035    }
036
037    public AbstractRosterItemAction(String s, Icon i, WindowInterface wi) {
038        super(s, i, wi);
039    }
040
041    Component mParent;
042
043    @Override
044    public void actionPerformed(ActionEvent event) {
045
046        // select the "from" entry/file
047        if (!selectFrom()) {
048            return;
049        }
050        // select the "to" entry/file
051        if (!selectTo()) {
052            return;
053        }
054        // transfer "from" to "to" as needed
055        if (!doTransfer()) {
056            return;
057        }
058        // update roster
059        updateRoster();
060    }
061
062    protected abstract boolean selectFrom();
063
064    abstract boolean selectTo();
065
066    abstract boolean doTransfer();
067
068    /**
069     * Common, but not unique implementation to add the "To" entry to the Roster
070     * and rewrite the roster file.
071     */
072    void updateRoster() {
073        addToEntryToRoster();
074    }
075
076    // variables to communicate the "from" entry, file, etc
077    String mFromID = null;
078    RosterEntry mFromEntry = null;
079    File mFromFile = null;
080    String mFromFilename = null;
081    String mFullFromFilename = null;  // includes path to preferences
082
083    // variables to communicate the "to" entry, file, etc.
084    String mToID = null;
085    RosterEntry mToEntry = null;
086    File mToFile = null;
087    String mToFilename = null;
088    String mFullToFilename = null; // includes path to preferences
089
090    boolean selectExistingFromEntry() {
091        // create a dialog to select the roster entry to copy
092        String group = null;
093        if (BeanUtil.hasProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP)) {
094            group = (String) BeanUtil.getProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP);
095        }
096        JComboBox<?> selections = new RosterEntryComboBox(group);
097        int retval = JmriJOptionPane.showOptionDialog(mParent,
098                Bundle.getMessage("CopyEntrySelectDialog"), Bundle.getMessage("CopyEntrySelectDialogTitle"),
099                JmriJOptionPane.DEFAULT_OPTION, JmriJOptionPane.INFORMATION_MESSAGE, null,
100                new Object[]{Bundle.getMessage("ButtonCancel"), Bundle.getMessage("ButtonOK"), selections}, null);
101        log.debug("Dialog value {} selected {}:\"{}\"", retval, selections.getSelectedIndex(), selections.getSelectedItem());
102        if (retval != 1) { // if not array position 1, ButtonOK
103            return false;  // user didn't select
104        }
105        // find the file for the selected entry to copy
106        setExistingEntry((RosterEntry) Objects.requireNonNull(selections.getSelectedItem()));
107
108        return true;
109    }
110
111    /**
112     * Set the roster entry this action acts upon.
113     *
114     * @param mFromEntry the roster entry to act upon
115     */
116    public void setExistingEntry(RosterEntry mFromEntry) {
117        this.mFromEntry = mFromEntry;
118        mFromFilename = mFromEntry.getFileName();
119        mFullFromFilename = Roster.getDefault().getRosterFilesLocation() + mFromFilename;
120        log.debug(" from resolves to \"{}\", \"{}\"", mFromFilename, mFullFromFilename);
121    }
122
123    boolean selectNewToEntryID() {
124        do {
125            // prompt for the new ID
126            mToID = JmriJOptionPane.showInputDialog(mParent, Bundle.getMessage("NewEntryDialog"),"");
127            if (mToID == null) {
128                return false;
129            }
130
131            // check for empty
132            if (mToID.isEmpty()) {
133                JmriJOptionPane.showMessageDialog(mParent, Bundle.getMessage("NewEntryEmptyWarn"));
134                // ask again
135                continue;
136            }
137
138            // check for duplicate
139            if (0 == Roster.getDefault().matchingList(null, null, null, null,
140                    null, null, mToID).size()) {
141                break;
142            }
143
144            // here it is a duplicate, reprompt
145            JmriJOptionPane.showMessageDialog(mParent, Bundle.getMessage("NewEntryDuplicateWarn"));
146
147        } while (true);
148        return true;
149    }
150
151    javax.swing.JFileChooser fileChooser;
152
153    boolean selectNewFromFile() {
154        if (fileChooser == null) {
155            fileChooser = jmri.jmrit.XmlFile.userFileChooser();
156        }
157        // refresh fileChooser view of directory, in case it changed
158        fileChooser.rescanCurrentDirectory();
159        int retVal = fileChooser.showOpenDialog(mParent);
160
161        // handle selection or cancel
162        if (retVal != JFileChooser.APPROVE_OPTION) {
163            return false;  // give up if no file selected
164        }
165        // call load to process the file
166        mFromFile = fileChooser.getSelectedFile();
167        mFromFilename = mFromFile.getName();
168        mFullFromFilename = mFromFile.getAbsolutePath();
169        log.debug("New from file: {} at {}", mFromFilename, mFullFromFilename); // NOI18N
170        return true;
171    }
172
173    boolean selectNewToFile() {
174        if (fileChooser == null) {
175            fileChooser = jmri.jmrit.XmlFile.userFileChooser();
176        }
177        fileChooser.setSelectedFile(new File(mFromFilename));
178        int retVal = fileChooser.showSaveDialog(mParent);
179
180        // handle selection or cancel
181        if (retVal != JFileChooser.APPROVE_OPTION) {
182            return false;  // give up if no file selected
183        }
184        // call load to process the file
185        mToFile = fileChooser.getSelectedFile();
186        mToFilename = mToFile.getName();
187        mFullToFilename = mToFile.getAbsolutePath();
188        log.debug("New to file: {} at {}", mToFilename, mFullToFilename); // NOI18N
189        return true;
190    }
191
192    void addToEntryToRoster() {
193        // add the new entry to the roster & write it out
194        Roster.getDefault().addEntry(mToEntry);
195        Roster.getDefault().writeRoster();
196    }
197
198    // never invoked, because we overrode actionPerformed above
199    @Override
200    public jmri.util.swing.JmriPanel makePanel() {
201        throw new IllegalArgumentException("Should not be invoked");
202    }
203
204    // initialize logging
205    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractRosterItemAction.class);
206
207}