001 /*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2013
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 http://www.gnu.org/licenses.
027 */
028 package edu.wisc.ssec.mcidasv.chooser;
029
030 import java.awt.BorderLayout;
031 import java.io.File;
032
033 import java.text.ParseException;
034 import java.text.SimpleDateFormat;
035 import java.util.Date;
036 import java.util.Vector;
037
038 import javax.swing.JFileChooser;
039 import javax.swing.JOptionPane;
040 import javax.swing.JPanel;
041 import javax.swing.filechooser.FileFilter;
042
043 import org.slf4j.Logger;
044 import org.slf4j.LoggerFactory;
045
046 import ucar.unidata.idv.chooser.IdvChooserManager;
047 import ucar.unidata.util.StringUtil;
048
049 public class SuomiNPPChooser extends FileChooser {
050
051 private static final long serialVersionUID = 1L;
052 private static final Logger logger = LoggerFactory.getLogger(SuomiNPPChooser.class);
053 private static final long CONSECUTIVE_GRANULE_MAX_GAP_MS = 60000;
054
055 // date formatter for converting Suomi NPP day/time from file name for consecutive granule check
056 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmSSS");
057
058 /**
059 * Create the chooser with the given manager and xml
060 *
061 * @param mgr The manager
062 * @param root The xml
063 *
064 */
065
066 public SuomiNPPChooser(IdvChooserManager mgr, org.w3c.dom.Element root) {
067 super(mgr, root);
068 }
069
070 /**
071 * Make the file chooser
072 *
073 * @param path the initial path
074 *
075 * @return the file chooser
076 */
077
078 protected JFileChooser doMakeFileChooser(String path) {
079 if (fileChooser == null) {
080 logger.debug("Creating Suomi NPP File Chooser...");
081 fileChooser = new SuomiNPPFileChooser(path, this);
082 } else {
083 logger.debug("2nd call to doMakeFileChooser, why?");
084 }
085 return fileChooser;
086 }
087
088 /**
089 * Handle the selection of the set of files
090 *
091 * @param files The files the user chose
092 * @param directory The directory they chose them from
093 * @return True if the file was successful
094 * @throws Exception
095 */
096
097 protected boolean selectFilesInner(File[] files, File directory)
098 throws Exception {
099 if ((files == null) || (files.length == 0)) {
100 userMessage("Please select a file");
101 return false;
102 }
103
104 // At present, Suomi NPP chooser only allows selecting sets of consecutive granules
105 boolean granulesAreConsecutive = testConsecutiveGranules(files);
106 if (granulesAreConsecutive) {
107 return super.selectFilesInner(files, directory);
108 } else {
109 // throw up a dialog to tell user the problem
110 JOptionPane.showMessageDialog(this, "When selecting multiple granules, they must be consecutive.");
111 }
112 return false;
113 }
114
115 /**
116 * Test whether a set of files are consecutive Suomi NPP granules,
117 * any sensor.
118 * @param files
119 * @return true if consecutive tests pass for all files
120 */
121
122 private boolean testConsecutiveGranules(File[] files) {
123 boolean testResult = false;
124 if (files == null) return testResult;
125 // compare start time of current granule with end time of previous
126 // difference should be very small - under a second
127 long prvTime = -1;
128 testResult = true;
129 for (int i = 0; i < files.length; i++) {
130 if ((files[i] != null) && !files[i].isDirectory()) {
131 if (files[i].exists()) {
132 String fileName = files[i].getName();
133 int dateIndex = fileName.lastIndexOf("_d2") + 2;
134 int timeIndexStart = fileName.lastIndexOf("_t") + 2;
135 int timeIndexEnd = fileName.lastIndexOf("_e") + 2;
136 String dateStr = fileName.substring(dateIndex, dateIndex + 8);
137 String timeStrStart = fileName.substring(timeIndexStart, timeIndexStart + 7);
138 String timeStrEnd = fileName.substring(timeIndexEnd, timeIndexEnd + 7);
139 // sanity check on file name lengths
140 int fnLen = fileName.length();
141 if ((dateIndex > fnLen) || (timeIndexStart > fnLen) || (timeIndexEnd > fnLen)) {
142 logger.warn("unexpected file name length for: " + fileName);
143 testResult = false;
144 break;
145 }
146 // pull start and end time out of file name
147 Date dS = null;
148 Date dE = null;
149 try {
150 dS = sdf.parse(dateStr + timeStrStart);
151 // due to nature of Suomi NPP file name encoding, we need a special
152 // check here - end time CAN roll over to next day, while day part
153 // does not change. if this happens, we tweak the date string
154 String endDateStr = dateStr;
155 String startHour = timeStrStart.substring(0, 2);
156 String endHour = timeStrEnd.substring(0, 2);
157 if ((startHour.equals("23")) && (endHour.equals("00"))) {
158 // temporarily convert date to integer, increment, convert back
159 int tmpDate = Integer.parseInt(dateStr);
160 tmpDate++;
161 endDateStr = "" + tmpDate;
162 logger.info("Granule time spanning days case handled ok...");
163 }
164 dE = sdf.parse(endDateStr + timeStrEnd);
165 } catch (ParseException e) {
166 logger.error("Not recognized as valid Suomi NPP file name: " + fileName);
167 testResult = false;
168 break;
169 }
170 long curTime = dS.getTime();
171 long endTime = dE.getTime();
172 // only check current with previous
173 if (prvTime > 0) {
174 // make sure time diff does not exceed allowed threshold
175 // consecutive granules should be less than 1 minute apart
176 if ((curTime - prvTime) > CONSECUTIVE_GRANULE_MAX_GAP_MS) {
177 testResult = false;
178 break;
179 }
180 }
181 prvTime = endTime;
182 }
183 }
184 }
185 return testResult;
186 }
187
188 /**
189 * Convert the given array of File objects
190 * to an array of String file names. Only
191 * include the files that actually exist.
192 *
193 * @param files Selected files
194 * @return Selected files as Strings
195 */
196
197 protected String[] getFileNames(File[] files) {
198 if (files == null) {
199 return (String[]) null;
200 }
201 Vector<String> v = new Vector<String>();
202 String fileNotExistsError = "";
203
204 // NOTE: If multiple files are selected, then missing files
205 // are not in the files array. If one file is selected and
206 // it is not there, then it is in the array and file.exists()
207 // is false
208 for (int i = 0; i < files.length; i++) {
209 if ((files[i] != null) && !files[i].isDirectory()) {
210 if ( !files[i].exists()) {
211 fileNotExistsError += "File does not exist: " + files[i] + "\n";
212 } else {
213 v.add(files[i].toString());
214 }
215 }
216 }
217
218 if (fileNotExistsError.length() > 0) {
219 userMessage(fileNotExistsError);
220 return null;
221 }
222
223 return v.isEmpty()
224 ? null
225 : StringUtil.listToStringArray(v);
226 }
227
228 /**
229 * Get the bottom panel for the chooser
230 * @return the bottom panel
231 */
232
233 protected JPanel getBottomPanel() {
234 // No bottom panel at present
235 return null;
236 }
237
238 /**
239 * Get the center panel for the chooser
240 * @return the center panel
241 */
242
243 protected JPanel getCenterPanel() {
244 JPanel centerPanel = super.getCenterPanel();
245
246 JPanel jp = new JPanel(new BorderLayout()) {
247 public void paint(java.awt.Graphics g) {
248 FileFilter ff = fileChooser.getFileFilter();
249 if (! (ff instanceof SuomiNPPFilter)) {
250 fileChooser.setAcceptAllFileFilterUsed(false);
251 fileChooser.setFileFilter(new SuomiNPPFilter());
252 }
253 super.paint(g);
254 }
255 };
256 jp.add(centerPanel);
257
258 return jp;
259 }
260
261 }