001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.chooser;
030
031import java.awt.Component;
032import java.awt.Container;
033import java.awt.event.ActionEvent;
034import java.awt.event.ActionListener;
035import java.beans.PropertyChangeEvent;
036import java.beans.PropertyChangeListener;
037import java.io.File;
038import java.util.ArrayList;
039import java.util.Collections;
040import java.util.Objects;
041import java.util.Vector;
042
043import javax.swing.JComboBox;
044import javax.swing.JFileChooser;
045import javax.swing.JLabel;
046import javax.swing.JPanel;
047import javax.swing.filechooser.FileFilter;
048
049import org.w3c.dom.Element;
050
051import ucar.unidata.idv.chooser.IdvChooserManager;
052import ucar.unidata.util.GuiUtils;
053import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
054
055public class HRITChooser extends FileChooser {
056
057        private static final long serialVersionUID = 1L;
058    
059    private HRITFilter hf = null;
060
061    private JLabel channelLabel = McVGuiUtils.makeLabelRight("Data Channel:");
062
063    /**
064     * Create the chooser with the given manager and xml
065     *
066     * @param mgr The manager
067     * @param root The xml
068     *
069     */
070    
071    public HRITChooser(IdvChooserManager mgr, Element root) {
072        super(mgr, root);
073    }
074    
075    /**
076     * Make the file chooser
077     *
078     * @param path   the initial path
079     *
080     * @return  the file chooser
081     */
082    
083    protected JFileChooser doMakeFileChooser(String path) {
084        return new HRITFileChooser(path);
085    }
086    
087    protected Vector<String> getAvailableHRITTypes(String path) {
088        if (path == null) path = ".";
089        
090        ArrayList<String> al = new ArrayList<String>();
091        Vector<String> v = new Vector<String>();
092        File f = new File(path);
093        File [] files = null;
094        if (f.isDirectory()) {
095                files = f.listFiles(
096                        new java.io.FileFilter() {
097                                public boolean accept(File f) {
098                            if ((f.getName().endsWith("__")) && (f.getName().matches(".*MSG[2-3].*"))) {
099                                return true;
100                            } else {
101                                return false;
102                            }
103                                }
104                        }
105                );
106        }
107        if (files != null) {
108                for (int i = 0; i < files.length; i++) {
109                String channelStr = files[i].getName().substring(26, 32);
110                if (channelStr.equals("______")) continue;
111                channelStr = channelStr.replace("___", "");
112                if (! al.contains(channelStr)) {
113                        al.add(channelStr);
114                }
115                }
116        }
117        Collections.sort(al);
118        for (int i = 0; i < al.size(); i++) {
119                v.add(al.get(i));
120        }
121        return v;
122    }
123    
124    public class ImageTypeChooser extends JComboBox implements ActionListener, PropertyChangeListener {
125
126        private static final long serialVersionUID = 1L;
127        JFileChooser jfc = null;
128        
129        public ImageTypeChooser(JFileChooser fc, String path) {
130            jfc = fc;
131            Vector<String> availableTypes = getAvailableHRITTypes(path);
132            if (availableTypes.size() == 1 && Objects.equals(availableTypes.get(0), ".")) {
133                availableTypes.removeAllElements();
134            }
135            reloadComboBox(availableTypes);
136            addActionListener(this);
137            McVGuiUtils.setComponentWidth(this, McVGuiUtils.Width.DOUBLE);
138        }
139        
140        public void actionPerformed(ActionEvent e) {
141            JComboBox cb = (JComboBox) e.getSource();
142            String newFilter = (String) cb.getSelectedItem();
143            HRITFilter hFilter = (HRITFilter) jfc.getFileFilter();
144            hFilter.setExtraFilter(newFilter);
145            jfc.rescanCurrentDirectory();
146        }
147        
148        public void reloadComboBox(Vector<String> v) {
149            removeAllItems();
150            if (v != null) {
151                for (int i = 0; i < v.size(); i++) {
152                    addItem(v.get(i));
153                }
154            }
155            if (v == null || v.size() == 0) {
156                setEnabled(false);
157                channelLabel.setEnabled(false);
158            } else {
159                setEnabled(true);
160                channelLabel.setEnabled(true);
161            }
162        }
163        
164        public void propertyChange(PropertyChangeEvent e) {
165            String prop = e.getPropertyName();
166
167            // If the directory changed, reload the combo box with new image type choices.
168            if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop)) {
169                Vector<String> availableTypes = getAvailableHRITTypes(jfc.getCurrentDirectory().getPath());
170                reloadComboBox(availableTypes);
171            }
172
173        }
174        
175    }
176    
177    /* HRITFilter */
178    public class HRITFilter extends FileFilter {
179        
180        String extraFilter = null;
181        
182        public HRITFilter(String extraFilter) {
183                super();
184                if (extraFilter != null) {
185                        this.extraFilter = extraFilter;
186                }
187        }
188        
189        // Accept all directories and all HRIT files.
190        public boolean accept(File f) {
191                
192            if (f.isDirectory()) {
193                return true;
194            }
195
196            // XXX TJJ - at present, we are ONLY allowing MSG2 and MSG3 segment data files
197            // through the filter which have already been Wavelet decompressed
198            // (i.e., they end with __ and not C_ )
199            String fileName = f.getName();
200            if ((fileName.endsWith("__")) && (fileName.matches(".*MSG[2-3].*")) && (fileName.length() >= 58)) {
201                if (extraFilter != null) {
202                        if (fileName.contains(extraFilter)) {
203                                return true;
204                        } else {
205                                return false;
206                        }
207                }
208            } else {
209                return false;
210            }
211            return false;
212
213        }
214
215        // The description of this filter
216        public String getDescription() {
217            return "HRIT Data";
218        }
219        
220        // change the additional filter string
221        public void setExtraFilter(String newFilter) {
222                if (newFilter != null) {
223                        extraFilter = newFilter;
224                        //seenPatterns.clear();
225                }
226        }
227        
228    }
229        
230    /**
231     * HRITFileChooser, an extension of JFileChooser
232     *
233     * @author Tommy Jasmin
234     */
235    
236    public class HRITFileChooser extends JFileChooser {
237
238        /**
239                 * default for serializable class
240                 */
241                private static final long serialVersionUID = 1L;
242
243                /**
244         * Create the file chooser
245         *
246         * @param path   the initial path
247         */
248                
249        public HRITFileChooser(String path) {
250            super(path);
251            setControlButtonsAreShown(false);
252            setMultiSelectionEnabled(false);
253            setAcceptAllFileFilterUsed(false);
254            processChildren(this);
255        }
256
257        private void processChildren(Container c) {
258                Component [] components = c.getComponents();
259                if (components != null) {
260                        // loop through all components, looking for the JLabel children of 
261                        // components we want to remove
262                        for (int i = 0; i < components.length; i++) {
263                                if (components[i] instanceof JLabel) {
264                                        String text = ((JLabel) components[i]).getText();
265                                        if (text.equals("File Name:")) {
266                                                hideChildren((Container) components[i].getParent());
267                                                continue;
268                                        }
269                                        if (text.equals("Files of Type:")) {
270                                                hideChildren((Container) components[i].getParent());
271                                                continue;
272                                        }
273                                }
274                                // now check this component for any children
275                                processChildren((Container) components[i]);
276                        }
277                }
278        }
279        
280        private void hideChildren(Container c) {
281                Component [] components = c.getComponents();
282                for (int i = 0; i < components.length; i++) {
283                        components[i].setVisible(false);
284                }
285                c.setVisible(false);
286        }
287
288        /**
289         * Approve the selection
290         */
291        
292        public void approveSelection() {
293            HRITChooser.this.doLoad();
294        }
295
296        /**
297         * Cancel the selection
298         */
299        
300        public void cancelSelection() {
301            closeChooser();
302        }
303
304        /**
305         * Set the selected files
306         *
307         * @param selectedFiles  the selected files
308         */
309        
310        public void setSelectedFiles(File[] selectedFiles) {
311                String channelStr = null;
312                String timeStr = null;
313                if (selectedFiles != null) {
314                        for (int i = 0; i < selectedFiles.length; i++) {
315                                if (! selectedFiles[i].isDirectory()) {
316                                        if (selectedFiles[i].getName().length() >= 58) {
317                                                channelStr = selectedFiles[i].getName().substring(26, 32);
318                                                timeStr = selectedFiles[i].getName().substring(46, 58);
319                                        }
320                                }
321                        }
322                }
323                File curDir = getCurrentDirectory();
324                File [] fileList = curDir.listFiles();
325                String tmpChannel = null;
326                String tmpTime = null;
327                ArrayList<File> matches = new ArrayList<File>();
328                for (int i = 0; i < fileList.length; i++) {
329                        if ((fileList[i].getName().endsWith("__")) && 
330                                (fileList[i].getName().matches(".*MSG[2-3].*")) && 
331                                (fileList[i].getName().length() >= 58)) {
332                                tmpChannel = fileList[i].getName().substring(26, 32);
333                                tmpTime = fileList[i].getName().substring(46, 58);
334                                if ((tmpChannel.equals(channelStr)) && (tmpTime.equals(timeStr))) {
335                                        matches.add(fileList[i]);
336                                }
337                        }
338                }
339                Collections.sort(matches);
340                
341                // make new file array from ArrayList matches
342                File [] fileSet = new File[matches.size()];
343                for (int i = 0; i < matches.size(); i++) {
344                        fileSet[i] = (File) matches.get(i);
345                }
346
347                super.setSelectedFiles(fileSet);
348            setHaveData( !((selectedFiles == null)
349                           || (selectedFiles.length == 0)));
350        }
351    }
352
353    /**
354     * Handle the selection of the set of files
355     *
356     * @param files The files the user chose
357     * @param directory The directory they chose them from
358     * @return True if the file was successful
359     * @throws Exception
360     */
361    
362    protected boolean selectFilesInner(File[] files, File directory)
363            throws Exception {
364        if ((files == null) || (files.length == 0)) {
365            userMessage("Please select a file");
366            return false;
367        }
368        
369        // only allow selection of files that make sense as a "set"
370        // for now, that means all part of single image for a single channel
371        String channelStr = files[0].getName().substring(26, 32);
372        String timeStr = files[0].getName().substring(46, 58);
373        int prvSegment = -1;
374        int curSegment = -1;
375        for (int i = 0; i < files.length; i++) {
376                try {
377                        curSegment = Integer.parseInt(files[i].getName().substring(40, 42));
378                } catch (NumberFormatException nfe) {
379                        userMessage("Problem determining image segment number for file: " + files[i].getName());
380                        return false;
381                }
382                if (!files[i].getName().substring(26, 32).equals(channelStr)) {
383                        userMessage("Selected data must be for a single channel and time");
384                        return false;
385                }
386                if (!files[i].getName().substring(46, 58).equals(timeStr)) {
387                        userMessage("Selected data must be for a single channel and time");
388                        return false;
389                }
390                if (prvSegment >= 0) {
391                        if (curSegment != (prvSegment + 1)) {
392                        userMessage("Selected data must be a contiguous set of image segment files");
393                        return false;
394                        }
395                }
396                prvSegment = curSegment;
397        }
398        
399        // XXX TJJ - until HRITAdapter can handle the fact that HRV data can 
400        // alter geographic coverage in mid-transmission, we deal with this by
401        // only allowing display of HRV segments one at a time.
402        
403        // commenting out for now - will deal with it in field selector window
404/*        if (files.length > 1) {
405                int hrvCount = 0;
406                for (int i = 0; i < files.length; i++) {
407                        if (files[i].getName().contains("HRV")) {
408                                hrvCount++;
409                        } 
410                        if (hrvCount > 1) {
411                                userMessage("At present, HRV data can only be displayed one file at a time");
412                                return false;
413                        }                       
414                }
415        }*/
416
417        return super.selectFilesInner(files, directory);
418    }
419    
420    /**
421     * Get the bottom panel for the chooser
422     * @return the bottom panel
423     */
424    
425    protected JPanel getBottomPanel() {
426        // If we don't have a fileChooser yet, this won't do any good
427        // This happens when Unidata's FileChooser is instantiated
428        // We instantiate ours right after that
429        if (fileChooser == null) {
430                return null;
431        }
432        
433        ImageTypeChooser itc = new ImageTypeChooser(fileChooser, getPath());
434        fileChooser.addPropertyChangeListener(itc);
435        JPanel bottomPanel = GuiUtils.left(itc);
436        bottomPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
437        
438        return McVGuiUtils.makeLabeledComponent(channelLabel, itc);
439    }
440    
441    /**
442     * Get the center panel for the chooser
443     * @return the center panel
444     */
445    
446    protected JPanel getCenterPanel() {
447        JPanel centerPanel = super.getCenterPanel();
448        
449        fileChooser.setAcceptAllFileFilterUsed(false);
450      
451        // see what HRIT data is available in this directory,
452        Vector<String> availableTypes = getAvailableHRITTypes(getPath());
453        String extraFilter = null;
454        if ((availableTypes != null) && (availableTypes.size() > 0)) {
455                extraFilter = (String) availableTypes.get(0);
456        }
457        
458        hf = new HRITFilter(extraFilter);
459        fileChooser.setFileFilter(hf);
460
461        return centerPanel;
462    }
463    
464}