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 }