001    /*
002     * $Id: SuomiNPPChooser.java,v 1.3 2012/03/29 20:44:30 tommyj Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    package edu.wisc.ssec.mcidasv.chooser;
031    
032    import java.awt.Component;
033    import java.awt.Container;
034    
035    import java.io.File;
036    
037    import java.text.ParseException;
038    import java.text.SimpleDateFormat;
039    import java.util.Date;
040    import java.util.Vector;
041    
042    import javax.swing.JFileChooser;
043    import javax.swing.JLabel;
044    import javax.swing.JOptionPane;
045    import javax.swing.JPanel;
046    
047    import org.slf4j.Logger;
048    import org.slf4j.LoggerFactory;
049    
050    import ucar.unidata.idv.chooser.IdvChooserManager;
051    import ucar.unidata.util.StringUtil;
052    
053    public class SuomiNPPChooser extends FileChooser {
054            
055            private static final long serialVersionUID = 1L;
056            private static final Logger logger = LoggerFactory.getLogger(SuomiNPPChooser.class);
057            private static final long CONSECUTIVE_GRANULE_TIME_GAP_MS = 1000;
058            
059            // date formatter for converting Suomi NPP day/time from file name for consecutive granule check
060        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmSSS");
061    
062        /**
063         * Create the chooser with the given manager and xml
064         *
065         * @param mgr The manager
066         * @param root The xml
067         *
068         */
069        
070        public SuomiNPPChooser(IdvChooserManager mgr, org.w3c.dom.Element root) {
071            super(mgr, root);
072        }
073       
074        /**
075         * Make the file chooser
076         *
077         * @param path   the initial path
078         *
079         * @return  the file chooser
080         */
081        
082        protected JFileChooser doMakeFileChooser(String path) {
083            if (fileChooser == null) {
084                    logger.trace("Creating Suomi NPP File Chooser...");
085                    fileChooser = new SuomiNPPFileChooser(path);
086            } else {
087                    logger.trace("2nd call to doMakeFileChooser, why?");
088            }
089            return fileChooser;
090        }
091            
092        /**
093         * An extension of JFileChooser
094         *
095         * @author Tommy Jasmin
096         */
097        
098        public class SuomiNPPFileChooser extends JFileChooser {
099    
100            /**
101                     * default for serializable class
102                     */
103                    private static final long serialVersionUID = 1L;
104    
105                    /**
106             * Create the file chooser
107             *
108             * @param path   the initial path
109             */
110            public SuomiNPPFileChooser(String path) {
111                super(path);
112                setControlButtonsAreShown(false);
113                setMultiSelectionEnabled(true);
114                setAcceptAllFileFilterUsed(false);
115                processChildren(this);
116            }
117    
118            private void processChildren(Container c) {
119                    Component [] components = c.getComponents();
120                    if (components != null) {
121                            // loop through all components, looking for the JLabel children of 
122                            // components we want to remove
123                            for (int i = 0; i < components.length; i++) {
124                                    if (components[i] instanceof JLabel) {
125                                            String text = ((JLabel) components[i]).getText();
126                                            if (text.equals("File Name:")) {
127                                                    hideChildren((Container) components[i].getParent());
128                                                    continue;
129                                            }
130                                            if (text.equals("Files of Type:")) {
131                                                    hideChildren((Container) components[i].getParent());
132                                                    continue;
133                                            }
134                                    }
135                                    // now check this component for any children
136                                    processChildren((Container) components[i]);
137                            }
138                    }
139            }
140            
141            private void hideChildren(Container c) {
142                    Component [] components = c.getComponents();
143                    for (int i = 0; i < components.length; i++) {
144                            components[i].setVisible(false);
145                    }
146                    c.setVisible(false);
147            }
148    
149            /**
150             * Approve the selection
151             */
152            public void approveSelection() {
153                SuomiNPPChooser.this.doLoad();
154            }
155    
156            /**
157             * Cancel the selection
158             */
159            public void cancelSelection() {
160                closeChooser();
161            }
162    
163            /**
164             * Set the selected files
165             *
166             * @param selectedFiles  the selected files
167             */
168            public void setSelectedFiles(File[] selectedFiles) {
169                    super.setSelectedFiles(selectedFiles);
170                setHaveData( !((selectedFiles == null) || (selectedFiles.length == 0)));
171            }
172        }
173    
174        /**
175         * Handle the selection of the set of files
176         *
177         * @param files The files the user chose
178         * @param directory The directory they chose them from
179         * @return True if the file was successful
180         * @throws Exception
181         */
182        
183        protected boolean selectFilesInner(File[] files, File directory)
184                throws Exception {
185            if ((files == null) || (files.length == 0)) {
186                userMessage("Please select a file");
187                return false;
188            }
189    
190            // At present, Suomi NPP chooser only allows selecting sets of consecutive granules
191            boolean granulesAreConsecutive = testConsecutiveGranules(files);
192            if (granulesAreConsecutive) {
193                    return super.selectFilesInner(files, directory);
194            } else {
195                    // throw up a dialog to tell user the problem
196                    JOptionPane.showMessageDialog(this, "When selecting multiple granules, they must be consecutive.");
197            }
198            return false;
199        }
200    
201        /**
202         * Test whether a set of files are consecutive Suomi NPP granules,
203         * any sensor.
204         * @param files
205         * @return true if consecutive tests pass for all files
206         */
207        
208        private boolean testConsecutiveGranules(File[] files) {
209            boolean testResult = false;
210            if (files == null) return testResult;
211                    // compare start time of current granule with end time of previous
212            // difference should be very small - under a second
213            long prvTime = -1;
214            testResult = true;
215            for (int i = 0; i < files.length; i++) {
216                if ((files[i] != null) && !files[i].isDirectory()) {
217                    if (files[i].exists()) {
218                            String fileName = files[i].getName(); 
219                            int dateIndex = fileName.lastIndexOf("_d2") + 2;
220                            int timeIndexStart = fileName.lastIndexOf("_t") + 2;
221                            int timeIndexEnd = fileName.lastIndexOf("_e") + 2;
222                            String dateStr = fileName.substring(dateIndex, dateIndex + 8);
223                            String timeStrStart = fileName.substring(timeIndexStart, timeIndexStart + 7);
224                            String timeStrEnd = fileName.substring(timeIndexEnd, timeIndexEnd + 7);
225                            // sanity check on file name lengths
226                            int fnLen = fileName.length();
227                            if ((dateIndex > fnLen) || (timeIndexStart > fnLen) || (timeIndexEnd > fnLen)) {
228                                    logger.warn("unexpected file name length for: " + fileName);
229                                    testResult = false;
230                                    break;
231                            }
232                        // pull start and end time out of file name
233                        Date dS = null;
234                        Date dE = null;
235                        try {
236                                                    dS = sdf.parse(dateStr + timeStrStart);
237                                                    dE = sdf.parse(dateStr + timeStrEnd);
238                                            } catch (ParseException e) {
239                                                    logger.error("Not recognized as valid Suomi NPP file name: " + fileName);
240                                                    testResult = false;
241                                                    break;
242                                            }
243                                            long curTime = dS.getTime();
244                                            long endTime = dE.getTime();
245                                            // only check current with previous
246                                            if (prvTime > 0) {
247                                                    // make sure time diff does not exceed allowed threshold
248                                                    // consecutive granules should be less than 1 second apart
249                                                    if ((curTime - prvTime) > CONSECUTIVE_GRANULE_TIME_GAP_MS) {
250                                                            testResult = false;
251                                                            break;
252                                                    }
253                                            }
254                                            prvTime = endTime;
255                    }
256                }
257            }
258                    return testResult;
259            }
260    
261            /**
262         * Convert the given array of File objects
263         * to an array of String file names. Only
264         * include the files that actually exist.
265         *
266         * @param files Selected files
267         * @return Selected files as Strings
268         */
269        
270        protected String[] getFileNames(File[] files) {
271            if (files == null) {
272                return (String[]) null;
273            }
274            Vector<String> v = new Vector<String>();
275            String fileNotExistsError = "";
276    
277            // NOTE:  If multiple files are selected, then missing files
278            // are not in the files array.  If one file is selected and
279            // it is not there, then it is in the array and file.exists()
280            // is false
281            for (int i = 0; i < files.length; i++) {
282                if ((files[i] != null) && !files[i].isDirectory()) {
283                    if ( !files[i].exists()) {
284                        fileNotExistsError += "File does not exist: " + files[i] + "\n";
285                    } else {
286                        v.add(files[i].toString());
287                    }
288                }
289            }
290    
291            if (fileNotExistsError.length() > 0) {
292                userMessage(fileNotExistsError);
293                return null;
294            }
295    
296            return v.isEmpty()
297                   ? null
298                   : StringUtil.listToStringArray(v);
299        }
300        
301        /**
302         * Get the bottom panel for the chooser
303         * @return the bottom panel
304         */
305        
306        protected JPanel getBottomPanel() {
307            // No bottom panel at present
308            return null;
309        }
310        
311        /**
312         * Get the center panel for the chooser
313         * @return the center panel
314         */
315        
316        protected JPanel getCenterPanel() {
317            JPanel centerPanel = super.getCenterPanel();
318            
319            fileChooser.setAcceptAllFileFilterUsed(false);
320            fileChooser.setFileFilter(new SuomiNPPFilter());
321    
322            return centerPanel;
323        }
324        
325    }