package org.autoplot.hapi; import java.awt.Component; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.ParseException; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.swing.BoxLayout; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.JCheckBox; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; import org.das2.datum.TimeUtil; import org.das2.datum.Units; import org.das2.util.LoggerManager; import org.das2.util.TickleTimer; import org.das2.util.monitor.ProgressMonitor; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.autoplot.datasource.DataSourceEditorPanel; import org.autoplot.datasource.RecentComboBox; import org.autoplot.datasource.TimeRangeTool; import org.autoplot.datasource.URISplit; /** * Swing editor for HAPI URIs * @author jbf */ public final class HapiDataSourceEditorPanel extends javax.swing.JPanel implements DataSourceEditorPanel { private static final Logger logger= LoggerManager.getLogger("apdss.hapi"); private JSONArray idsJSON; private boolean supportsBinary; private URL defaultServer; private Datum myValidTime; private Component firstParameter=null; /** * return the range of available data. For example, Polar/Hydra data is available * from 1996-03-20 to 2008-04-15. * @param info * @return the range of available data. */ private DatumRange getRange( JSONObject info ) { try { if ( info.has("firstDate") && info.has("lastDate") ) { // this is deprecated behavior String firstDate= info.getString("firstDate"); String lastDate= info.getString("lastDate"); if ( firstDate!=null && lastDate!=null ) { Datum t1= Units.us2000.parse(firstDate); Datum t2= Units.us2000.parse(lastDate); if ( t1.le(t2) ) { return new DatumRange( t1, t2 ); } else { logger.warning( "firstDate and lastDate are out of order, ignoring."); } } } else if ( info.has("startDate") ) { // note startDate is required. String startDate= info.getString("startDate"); String stopDate; if ( info.has("stopDate") ) { stopDate= info.getString("stopDate"); } else { stopDate= null; } if ( stopDate!=null ) { stopDate= HapiDataSource.parseTime(stopDate).toString(); } if ( startDate!=null ) { Datum t1= Units.us2000.parse(startDate); Datum t2= stopDate==null ? myValidTime : Units.us2000.parse(stopDate); if ( t1.le(t2) ) { return new DatumRange( t1, t2 ); } else { logger.warning( "firstDate and lastDate are out of order, ignoring."); } } } } catch ( JSONException | ParseException ex ) { logger.log( Level.WARNING, ex.getMessage(), ex ); } return null; } private String currentParameters= null; private URL currentServer= null; private DatumRange currentRange= null; private String currentId= null; private String currentExtra=null; private int lastParamIndex= -1; // the index of the last parameter selection. /** * scientist-provided time range */ private String providedTimeRange= null; /** * Creates new form HapiDataSourceEditorPanel */ public HapiDataSourceEditorPanel() { try { myValidTime= TimeUtil.create( "2200-01-01T00:00" ); } catch (ParseException ex) { Logger.getLogger(HapiDataSourceEditorPanel.class.getName()).log(Level.SEVERE, null, ex); } try { List servers= HapiServer.getKnownServers(); this.defaultServer = new URL(servers.get(servers.size()-1)); } catch ( MalformedURLException ex ) { throw new RuntimeException(ex); } initComponents(); filtersComboBox.setPreferenceNode("hapi.filters"); timeRangeComboBox.setPreferenceNode(RecentComboBox.PREF_NODE_TIMERANGE); parametersScrollPane.getVerticalScrollBar().setUnitIncrement( parametersPanel.getFont().getSize() ); parametersPanel.setLayout( new BoxLayout( parametersPanel, BoxLayout.Y_AXIS ) ); serversComboBox.setEnabled(false); serversComboBox.setModel( new DefaultComboBoxModel<>( HapiServer.getKnownServersArray() ) ); loadKnownServersSoon(); idsList2.addListSelectionListener( new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if ( !e.getValueIsAdjusting() ) { String selectedValue= idsList2.getSelectedValue(); if ( selectedValue==null ) { return; } if ( !selectedValue.equals(currentId) ) { currentParameters= null; } if ( currentId!=null && currentId.equals(selectedValue) ) { return; } if ( currentServer!=null ) { currentId= selectedValue; } else { currentId= null; } if ( currentId==null ) { titleLabel.setText(" "); return; } if ( currentId.startsWith("Error:" ) ) { return; } titleLabel.setText("Retrieving info for "+currentId+"..."); parametersPanel.removeAll(); parametersPanel.revalidate(); parametersPanel.repaint(); resetVariableTimer.tickle(); } } } ); filtersComboBox.getEditor().getEditorComponent().addKeyListener( new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { Runnable run= new Runnable() { public void run() { final String search= (String)filtersComboBox.getEditor().getItem(); resetServerCatalog( currentServer, search ); } }; SwingUtilities.invokeLater(run); } } ); } TickleTimer resetVariableTimer= new TickleTimer( 100, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { try { String s= currentId; if ( s!=null && s.trim().length()>0 ) { resetId(HapiServer.encodeURL( (String)serversComboBox.getSelectedItem() ), s ); } else { parametersPanel.removeAll(); parametersPanel.add(new JLabel(" ")); // JEditorPane p= new JEditorPane(); // try { // p.setPage( new URL( (String)serversComboBox.getSelectedItem() )); // parametersPanel.add( p ); // } catch (IOException ex) { // Logger.getLogger(HapiDataSourceEditorPanel.class.getName()).log(Level.SEVERE, null, ex); // } titleLabel.setText(" "); } } catch (MalformedURLException ex) { JOptionPane.showMessageDialog( parametersPanel, ex.toString() ); } } }); /** * load the known servers and set the GUI. This should not be called from * the event thread, and a runnable for the event thread will be submitted. */ public void loadKnownServersImmediately() { final String[] servers= HapiServer.listHapiServersArray(); Runnable run= new Runnable() { @Override public void run() { serversComboBox.setModel( new DefaultComboBoxModel<>( servers )); try { defaultServer= new URL(servers[0]); //TODO: sometimes server is URL sometimes a string. How annoying... } catch (MalformedURLException ex) { logger.log(Level.SEVERE, null, ex); } if ( currentServer!=null ) { serversComboBox.setSelectedItem(currentServer.toString()); } else { serversComboBox.setSelectedIndex(0); } serversComboBox.setEnabled(true); } }; SwingUtilities.invokeLater(run); } /** * request that the known servers be displayed. This will spawn an * asynchronous thread to get the server names, and then will load the GUI * on the event thread. This can be called from the event thread. */ public void loadKnownServersSoon() { Runnable run= new Runnable() { @Override public void run() { loadKnownServersImmediately(); } }; new Thread(run,"loadKnownServers").start(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { jLabel1 = new javax.swing.JLabel(); serversComboBox = new javax.swing.JComboBox<>(); jLabel2 = new javax.swing.JLabel(); jButton1 = new javax.swing.JButton(); jSplitPane1 = new javax.swing.JSplitPane(); jPanel3 = new javax.swing.JPanel(); parametersScrollPane = new javax.swing.JScrollPane(); parametersPanel = new javax.swing.JPanel(); clearAllB = new javax.swing.JButton(); setAllB = new javax.swing.JButton(); extraInfoButton = new javax.swing.JButton(); titleLabel = new javax.swing.JLabel(); cachedFileButton = new javax.swing.JButton(); jPanel1 = new javax.swing.JPanel(); jScrollPane2 = new javax.swing.JScrollPane(); idsList2 = new javax.swing.JList<>(); clearButton = new javax.swing.JButton(); filtersComboBox = new org.autoplot.datasource.RecentComboBox(); messagesLabel = new javax.swing.JLabel(); binaryCB = new javax.swing.JCheckBox(); timeRangeComboBox = new org.autoplot.datasource.RecentComboBox(); exampleTimeRangesCB = new javax.swing.JComboBox<>(); disableCacheCheckBox = new javax.swing.JCheckBox(); jLabel1.setText("HAPI Server:"); serversComboBox.setEditable(true); serversComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "http://tsds.org/get/IMAGE/PT1M/hapi", " " })); serversComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { serversComboBoxActionPerformed(evt); } }); jLabel2.setText("Time Range: "); jButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/autoplot/hapi/calendar.png"))); // NOI18N jButton1.setToolTipText("Time Range Tool"); jButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButton1ActionPerformed(evt); } }); jSplitPane1.setDividerLocation(210); parametersPanel.setMinimumSize(new java.awt.Dimension(100, 0)); javax.swing.GroupLayout parametersPanelLayout = new javax.swing.GroupLayout(parametersPanel); parametersPanel.setLayout(parametersPanelLayout); parametersPanelLayout.setHorizontalGroup( parametersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 482, Short.MAX_VALUE) ); parametersPanelLayout.setVerticalGroup( parametersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 195, Short.MAX_VALUE) ); parametersScrollPane.setViewportView(parametersPanel); clearAllB.setText("Clear All"); clearAllB.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { clearAllBActionPerformed(evt); } }); setAllB.setText("Set All"); setAllB.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { setAllBActionPerformed(evt); } }); extraInfoButton.setText("Extra Info"); extraInfoButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { extraInfoButtonActionPerformed(evt); } }); titleLabel.setText(" "); cachedFileButton.setText("Cached Files..."); cachedFileButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cachedFileButtonActionPerformed(evt); } }); javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(parametersScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 486, Short.MAX_VALUE) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(clearAllB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(setAllB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cachedFileButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(extraInfoButton)) .addComponent(titleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(titleLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(parametersScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 188, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(clearAllB) .addComponent(setAllB) .addComponent(extraInfoButton) .addComponent(cachedFileButton))) ); jSplitPane1.setRightComponent(jPanel3); jScrollPane2.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); jScrollPane2.setMinimumSize(new java.awt.Dimension(100, 22)); idsList2.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); jScrollPane2.setViewportView(idsList2); clearButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/autoplot/hapi/clearTextButton.png"))); // NOI18N clearButton.setToolTipText("clear search bar"); clearButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { clearButtonActionPerformed(evt); } }); filtersComboBox.setToolTipText("search bar"); filtersComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { filtersComboBoxActionPerformed(evt); } }); javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(filtersComboBox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(clearButton)) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 183, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(clearButton) .addComponent(filtersComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 216, Short.MAX_VALUE)) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addGap(30, 30, 30) .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE))) ); jSplitPane1.setLeftComponent(jPanel1); messagesLabel.setText("(messages here)"); binaryCB.setText("Use Binary"); binaryCB.setToolTipText("Some servers support binary data transfers, and this will use binary to transfer data."); binaryCB.setEnabled(false); exampleTimeRangesCB.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Example Time Ranges" })); exampleTimeRangesCB.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { exampleTimeRangesCBItemStateChanged(evt); } }); disableCacheCheckBox.setText("Disable Cache"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 707, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(serversComboBox, 0, 1, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(timeRangeComboBox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(exampleTimeRangesCB, javax.swing.GroupLayout.PREFERRED_SIZE, 224, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addComponent(messagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(disableCacheCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(binaryCB))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel1) .addComponent(serversComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSplitPane1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(messagesLabel) .addComponent(binaryCB, javax.swing.GroupLayout.DEFAULT_SIZE, 25, Short.MAX_VALUE) .addComponent(disableCacheCheckBox)) .addGap(5, 5, 5) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel2) .addComponent(timeRangeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jButton1) .addComponent(exampleTimeRangesCB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); }// //GEN-END:initComponents private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed org.das2.util.LoggerManager.logGuiEvent(evt); TimeRangeTool tt= new TimeRangeTool(); tt.setSelectedRange(timeRangeComboBox.getText()); int r= JOptionPane.showConfirmDialog( this, tt, "Select Time Range", JOptionPane.OK_CANCEL_OPTION ); if ( r==JOptionPane.OK_OPTION) { timeRangeComboBox.setText(tt.getSelectedRange()); } }//GEN-LAST:event_jButton1ActionPerformed private void serversComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_serversComboBoxActionPerformed try { final URL url= HapiServer.encodeURL( (String)serversComboBox.getSelectedItem() ); if ( currentServer==null || !url.toExternalForm().equals(currentServer.toExternalForm()) ) { DefaultListModel m= new DefaultListModel() ; m.add(0,"Reading list of available datasets..."); idsList2.setModel( m ); } Runnable run= new Runnable() { @Override public void run() { try { resetServer( url ); } catch (IOException | JSONException ex) { logger.log(Level.SEVERE, null, ex); DefaultListModel m= new DefaultListModel() ; m.add(0,"Error: unable to connect"); idsList2.setModel( m ); } } }; new Thread( run, "resetServer").start(); } catch (MalformedURLException ex ) { logger.log(Level.SEVERE, null, ex); } }//GEN-LAST:event_serversComboBoxActionPerformed private void clearAllBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearAllBActionPerformed boolean first= !currentServer.toString().contains("https://cdaweb.gsfc.nasa.gov/registry/hdp/hapi"); for ( Component c: parametersPanel.getComponents() ) { if ( c instanceof JCheckBox ) { if ( first ) { ((JCheckBox)c).setSelected(true); first= false; } else { ((JCheckBox)c).setSelected(false); } } } }//GEN-LAST:event_clearAllBActionPerformed private void setAllBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_setAllBActionPerformed for ( Component c: parametersPanel.getComponents() ) { if ( c instanceof JCheckBox ) { ((JCheckBox)c).setSelected(true); } } }//GEN-LAST:event_setAllBActionPerformed private void extraInfoButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extraInfoButtonActionPerformed final JEditorPane jep= new JEditorPane(); jep.setContentType("text/html"); jep.setText( currentExtra ); jep.setEditable( false ); jep.setOpaque(false); jep.addHyperlinkListener( new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent hle) { // from http://stackoverflow.com/questions/14170041/is-it-possible-to-create-programs-in-java-that-create-text-to-link-in-chrome if (HyperlinkEvent.EventType.ACTIVATED.equals(hle.getEventType())) { Desktop desktop = Desktop.getDesktop(); try { desktop.browse(hle.getURL().toURI()); } catch (URISyntaxException | IOException ex) { logger.log( Level.WARNING, ex.getMessage(), ex ); } } } }); final JScrollPane p= new JScrollPane(jep); p.setPreferredSize( new Dimension( 800,400 ) ); p.setMaximumSize( new Dimension( 800,400 ) ); SwingUtilities.invokeLater(new Runnable() { // Grr stupid Java. @Override public void run() { jep.setCaretPosition(0); p.getVerticalScrollBar().setValue(0); } }); JOptionPane.showMessageDialog( this, p, "Extra Info", JOptionPane.INFORMATION_MESSAGE ); }//GEN-LAST:event_extraInfoButtonActionPerformed private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed filtersComboBox.setSelectedItem(""); }//GEN-LAST:event_clearButtonActionPerformed private void filtersComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_filtersComboBoxActionPerformed final String filter= filtersComboBox.getSelectedItem().toString(); Runnable run= new Runnable() { @Override public void run() { resetServerCatalog( currentServer, filter ); } }; new Thread( run, "resetServerCatalog2" ).start(); }//GEN-LAST:event_filtersComboBoxActionPerformed private void exampleTimeRangesCBItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_exampleTimeRangesCBItemStateChanged String s= (String)exampleTimeRangesCB.getSelectedItem(); if ( s.startsWith("Example") ) { //do nothing } else { timeRangeComboBox.setSelectedItem(s); } }//GEN-LAST:event_exampleTimeRangesCBItemStateChanged private void cachedFileButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cachedFileButtonActionPerformed String[] params= getParameters(true).split(","); Map ff; String str= (String)timeRangeComboBox.getSelectedItem(); String format= binaryCB.isSelected() ? "binary" : "csv"; try { DatumRange tr; if ( str==null ) { tr= currentRange; } else { tr= DatumRangeUtil.parseTimeRange(str); } if ( tr==null ) { JOptionPane.showMessageDialog(this,"id doesn't provide range"); return; } ff = HapiDataSource.getCacheFiles( this.currentServer, this.currentId, params, tr, format ); } catch ( ParseException ex ) { JOptionPane.showMessageDialog( this, "Unable to parse timerange: "+str); return; } if ( ff==null ) { JOptionPane.showMessageDialog( this, "No cache files found in the interval"); return; } File cacheFolder= HapiDataSource.cacheFolder( this.currentServer, "/data/" + this.currentId ); HapiCacheManager mm= new HapiCacheManager(); String[] ss= ff.keySet().toArray( new String[ff.size()] ); mm.setFiles( cacheFolder, ss ); if ( JOptionPane.showConfirmDialog(this,mm,"Manage Cached Data",JOptionPane.OK_CANCEL_OPTION)==JOptionPane.OK_OPTION ) { System.err.println("cacheFolder: "+cacheFolder ); for ( String s: ff.keySet() ) { File f1= new File( cacheFolder, s ); if ( !f1.delete() ) { logger.log(Level.INFO, "unable to delete {0}", f1); } } //FileUtil.deleteFileTree(cacheFolder); //TODO: off of the event thread } }//GEN-LAST:event_cachedFileButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JCheckBox binaryCB; private javax.swing.JButton cachedFileButton; private javax.swing.JButton clearAllB; private javax.swing.JButton clearButton; private javax.swing.JCheckBox disableCacheCheckBox; private javax.swing.JComboBox exampleTimeRangesCB; private javax.swing.JButton extraInfoButton; private org.autoplot.datasource.RecentComboBox filtersComboBox; private javax.swing.JList idsList2; private javax.swing.JButton jButton1; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel3; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JSplitPane jSplitPane1; private javax.swing.JLabel messagesLabel; private javax.swing.JPanel parametersPanel; private javax.swing.JScrollPane parametersScrollPane; private javax.swing.JComboBox serversComboBox; private javax.swing.JButton setAllB; private org.autoplot.datasource.RecentComboBox timeRangeComboBox; private javax.swing.JLabel titleLabel; // End of variables declaration//GEN-END:variables @Override public boolean reject(String uri) throws Exception { return false; } @Override public boolean prepare(String uri, Window parent, ProgressMonitor mon) throws Exception { URISplit split = URISplit.parse(uri); if ( split.file==null || split.file.equals("file:///") ) { // use TSDS's one by default. split.file= defaultServer.toString(); } if ( !split.file.endsWith("/hapi") ) { int i= split.file.lastIndexOf("/hapi"); if ( i>-1 ) { split.file= split.file.substring(0,i+5); } } try { serversComboBox.setSelectedItem(split.file); idsJSON= HapiServer.getCatalog(new URL(split.file)); } catch ( IOException ex ) { messagesLabel.setText("Unable to connect to server"); } return true; } /** make the currentParameters checklist reflect currentParameters spec. * * @param parameters comma-delineated list of currentParameters. */ private void setParameters( String parameters ) { for ( Component c: parametersPanel.getComponents() ) { if ( c instanceof JCheckBox ) { ((JCheckBox)c).setSelected(false); } } if ( parameters.length()>0 ) { String[] ss= parameters.split(","); int iparam=0; for ( Component c: parametersPanel.getComponents() ) { if ( c instanceof JCheckBox ) { String name= ((JCheckBox)c).getName(); ((JCheckBox)c).setSelected(false); for (String s : ss) { if (s.equals(name)) { ((JCheckBox)c).setSelected(true); if ( iparam>0 && firstParameter==null ) { firstParameter= c; } } } iparam++; } } } else { for ( Component c: parametersPanel.getComponents() ) { if ( c instanceof JCheckBox ) { ((JCheckBox)c).setSelected(true); } } } if ( firstParameter!=null ) { SwingUtilities.invokeLater( new Runnable() { public void run() { Rectangle r= firstParameter.getBounds(); parametersScrollPane.getViewport().setViewPosition( new Point( 0, Math.max( 0, r.y - parametersScrollPane.getHeight()/4 ) ) ); } }); } } /** * * @param enumerate if true show all parameters, instead of "" * @return all the parameters */ private String getParameters(boolean enumerate) { StringBuilder b= new StringBuilder(); boolean areAllTrue= true; for ( Component c: parametersPanel.getComponents() ) { if ( c instanceof JCheckBox ) { if ( ((JCheckBox)c).isSelected() ) { b.append(",").append(c.getName()); } else { areAllTrue= false; } } } if ( areAllTrue && !enumerate ) { return ""; } else { return b.substring(1); // remove first comma. } } @Override public void setURI(String uri) { URISplit split = URISplit.parse(uri); if ( split.file==null || split.file.equals("file:///") ) { // use TSDS's one by default. split.file= defaultServer.toString(); } else { if ( !split.file.endsWith("/hapi") ) { int i= split.file.lastIndexOf("/hapi"); if ( i>-1 ) { split.file= split.file.substring(0,i+5); } } try { currentServer= new URL(split.file); } catch (MalformedURLException ex) { logger.log(Level.SEVERE, null, ex); } } try { serversComboBox.setSelectedItem(HapiServer.decodeURL( HapiServer.encodeURL(split.file) ) ); } catch ( MalformedURLException ex ) { serversComboBox.setSelectedItem( split.file ); // do what we did before. } Map params= URISplit.parseParams( split.params ); String id= params.get("id"); if ( id!=null ) { try { id= URLDecoder.decode(id,"UTF-8"); idsList2.setSelectedValue( id, true ); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } currentId= id; } String timerange= params.get("timerange"); if ( timerange!=null ) { //timeRangeTextField.setText(timerange); timeRangeComboBox.setText(timerange); } providedTimeRange= timerange; String parameters= params.get("parameters"); if ( parameters!=null ) { parameters= HapiServer.decodeURLParameters(parameters); this.currentParameters= parameters; setParameters(this.currentParameters); resetVariableTimer.tickle("initialUpdate"); } else { resetVariableTimer.tickle("initialUpdateNoParams"); } if ( HapiSpec.BINARY.equals(params.get("format") ) ) { this.binaryCB.setSelected(true); } else { this.binaryCB.setSelected(false); } if ( !HapiServer.useCache() ) { cachedFileButton.setVisible(false); disableCacheCheckBox.setVisible(false); } disableCacheCheckBox.setSelected( "F".equals(params.get("cache")) ); messagesLabel.setText("Select dataset above"); } @Override public void markProblems(List problems) { } @Override public JPanel getPanel() { return this; } @Override public String getURI() { String parameters= getParameters(false); String id= idsList2.getSelectedValue(); if ( id==null ) { id= ""; } else { id = HapiServer.encodeURLParameters(id); //id= HapiServer.urlEncode(id); } String uri= "vap+hapi:"; try { // please encode the URLs before making Autoplot URIs, which really should be ASCII. uri = uri + HapiServer.encodeURL( serversComboBox.getSelectedItem().toString() ).toString(); } catch ( MalformedURLException ex ) { uri = uri + serversComboBox.getSelectedItem().toString(); } uri = uri + "?id=" + id + "&timerange="+timeRangeComboBox.getText().replaceAll(" ","+"); if ( binaryCB.isSelected() && binaryCB.isEnabled() ) { uri+= "&format=binary"; } if ( disableCacheCheckBox.isSelected() ) { uri+= "&cache=F"; } if ( parameters.length()>0 ) { return uri + "¶meters="+HapiServer.encodeURLParameters(parameters); } else { return uri; } } private void loadServerCapabilities( URL server ) throws JSONException { boolean binaryIsEnabled= false; try { JSONObject capabilitiesDoc= HapiServer.getCapabilities(server); if ( capabilitiesDoc.has(HapiSpec.OUTPUT_FORMATS ) ) { // new 2016-11-21. Other is deprecated. JSONArray outputFormats= capabilitiesDoc.getJSONArray(HapiSpec.OUTPUT_FORMATS ); for ( int i=0; i0 ) { Pattern p= Pattern.compile(filter,Pattern.CASE_INSENSITIVE); String id= catalogEntry.getString("id"); String title= null; if ( catalogEntry.has(HapiSpec.TITLE) ) { title= catalogEntry.getString(HapiSpec.TITLE); } if ( p.matcher(id).find() || ( title!=null && p.matcher(title).find() ) ) { model.addElement( catalogEntry.getString("id") ); maxCharacters= Math.max( catalogEntry.getString("id").length(), maxCharacters ); } } else { model.addElement( catalogEntry.getString("id") ); maxCharacters= Math.max( catalogEntry.getString("id").length(), maxCharacters ); } } idsList2.setModel( model ); int maxLenPixels= maxCharacters*8; // pixels per character maxLenPixels= Math.min( maxLenPixels,600 ); maxLenPixels= Math.max( maxLenPixels,300 ); jSplitPane1.setDividerLocation(maxLenPixels); if ( !String.valueOf(server).equals(String.valueOf(currentServer)) ) { // avoid name resolution. Thanks Findbugs! idsList2.setSelectedIndex(0); currentServer= server; idsList2.ensureIndexIsVisible(0); } else { if ( currentId!=null ) { idsList2.setSelectedValue( currentId, true ); } else { int i= idsList2.getSelectedIndex(); idsList2.ensureIndexIsVisible( i==-1 ? 0 : i ); } } binaryCB.setEnabled(supportsBinary); } catch ( JSONException ex ) { logger.log(Level.SEVERE, null, ex ); } } /** * get the catalog of the server. This should not be called from the event * thread. * @param server * @throws IOException * @throws JSONException */ private void resetServer( final URL server ) throws IOException, JSONException { idsJSON= HapiServer.getCatalog(server); loadServerCapabilities(server); Runnable run= new Runnable() { public void run() { resetServerCatalog(server,""); } }; SwingUtilities.invokeLater(run); } private String getHtmlFor( Object o ) throws JSONException { StringBuilder s= new StringBuilder(); if ( o instanceof JSONArray ) { JSONArray joa= (JSONArray)o; for ( int i=0; i"); } } else if ( o instanceof JSONObject ) { JSONObject jo= (JSONObject)o; s.append(""); Iterator iter= jo.keys(); String k; for ( ; iter.hasNext(); ) { k=iter.next().toString(); Object v= jo.get(k); String sv= (getHtmlFor(v)); s.append(""); } s.append("
").append(k).append("").append(sv).append("
"); } else if ( o instanceof String ) { String so= String.valueOf(o); if ( so.startsWith("spase:") ) { so= ""+so+""; } else if ( so.startsWith("http://") || so.startsWith("https://") || so.startsWith("ftp://" ) ) { so= ""+so+""; } else if ( so.startsWith("doi:") || so.startsWith("DOI:") ) { so= ""+so+""; } else if ( so.startsWith("10.") && so.length()>7 ) { Pattern p= Pattern.compile("(10[.][0-9]{3,}[^\\s\"/<>]*/[^\\s\"<>]+)"); if ( p.matcher(so).matches() ) { so= ""+so+""; } } s.append(so); } else { s.append(o.toString()); } return s.toString(); } /** * [yr,mon,day,hour,min,sec,nanos] * @param array * @return approximate seconds */ private static Datum cadenceArrayToDatum( int[] array ) { double seconds= array[6]/1e9; seconds+= array[5]; seconds+= array[4]*60; seconds+= array[3]*3600; seconds+= array[2]*86400; //approx, just to get scale seconds+= array[1]*86400*30; //approx, just to get scale seconds+= array[0]*86400*365; // approx, just to get scale return Units.seconds.createDatum(seconds); } /** * return the duration in a easily-human-consumable form. * @param milliseconds the duration in milliseconds. * @return a duration like "2.6 hours" */ public static String getDurationForHumans( long milliseconds ) { if ( milliseconds<2*1000 ) { return milliseconds+" milliseconds"; } else if ( milliseconds<2*60000 ) { return String.format( Locale.US, "%.1f",milliseconds/1000.)+" seconds"; } else if ( milliseconds<2*3600000 ) { return String.format( Locale.US, "%.1f",milliseconds/60000.)+" minutes"; } else if ( milliseconds<2*86400000 ) { return String.format( Locale.US, "%.1f",milliseconds/3600000.)+" hours"; } else { double ddays= milliseconds/86400000.; if ( ddays<48 ) { return String.format( Locale.US, "%.1f",ddays)+" days"; } else if ( ddays<400 ) { return String.format( Locale.US, "%.1f",ddays/7)+" weeks"; } else { return String.format( Locale.US, "%.1f",ddays/365)+" years"; } } } private void resetIdReportError( URL server, String id, Exception ex ) { logger.log(Level.SEVERE, null, ex); parametersPanel.removeAll(); parametersPanel.add(new javax.swing.JLabel("Error reported on server:")); String s= ex.getMessage(); parametersPanel.add(new javax.swing.JLabel(s)); JLabel space= new javax.swing.JLabel(" "); //space.setMinimumSize(new Dimension(30,30)); //space.setPreferredSize(new Dimension(30,30)); parametersPanel.add(space); final URL url= HapiServer.createURL(server, HapiSpec.INFO_URL, Collections.singletonMap(HapiSpec.URL_PARAM_ID, id ) ); javax.swing.JButton l= new javax.swing.JButton("Load URL in Browser"); l.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { Desktop.getDesktop().browse( url.toURI() ); } catch (URISyntaxException | IOException ex1) { logger.log(Level.SEVERE, null, ex1); } } }); parametersPanel.add(l); titleLabel.setText(""); } private void resetIdImmediately(String id,JSONObject info) throws JSONException { for ( JSONObject item : new JSONArrayIterator(idsJSON) ) { if ( item.getString("id").equals(id) ) { if ( item.has(HapiSpec.TITLE) ) { String title= item.getString(HapiSpec.TITLE); titleLabel.setText(title); titleLabel.setToolTipText(title); titleLabel.setMinimumSize(new Dimension(100,titleLabel.getFont().getSize())); } else { titleLabel.setText(id); } } } JSONArray parameters= info.getJSONArray("parameters"); StringBuilder extra= new StringBuilder(); extra.append(""); Iterator iter= info.keys(); String k; for ( ; iter.hasNext(); ) { k=iter.next().toString(); //if ( !k.equals("parameters") ) { Object v= info.get(k); extra.append(""); //} } extra.append("
").append(k).append(""); String s= getHtmlFor(v); if ( v.toString().length()>MAX_LENGTH_CHARACTERS ) { extra.append("(").append(v.toString().length()).append(" characters)"); //extra.append( s ) ; //v.toString() ); } else { extra.append( s ); } extra.append("
"); currentExtra= extra.toString(); parametersPanel.removeAll(); String[] sparams= new String[parameters.length()]; Boolean startRank2= null; for ( int i=0; i-1 ) { if ( lastParamIndex